Visual tweaks

This commit is contained in:
Bruno Bergher 2025-08-26 21:31:55 +01:00
parent 31fd2b9b15
commit 1fd6199f2a
5 changed files with 95 additions and 136 deletions

View file

@ -1,6 +1,6 @@
import { memo, useCallback, useMemo, useState } from "react"
import { Trans } from "react-i18next"
import { VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { Stamp, ListChecks, LayoutList } from "lucide-react"
import { vscode } from "@src/utils/vscode"
@ -23,8 +23,6 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
const portalContainer = useRooPortal("roo-portal")
const {
autoApprovalEnabled,
setAutoApprovalEnabled,
alwaysApproveResubmit,
setAlwaysAllowReadOnly,
setAlwaysAllowWrite,
@ -51,7 +49,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
[baseToggles, alwaysApproveResubmit],
)
const { hasEnabledOptions, effectiveAutoApprovalEnabled } = useAutoApprovalState(toggles, autoApprovalEnabled)
const { hasEnabledOptions, effectiveAutoApprovalEnabled } = useAutoApprovalState(toggles, true)
const onAutoApproveToggle = useCallback(
(key: AutoApproveSetting, value: boolean) => {
@ -90,30 +88,9 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
setAlwaysAllowUpdateTodoList(value)
break
}
// Check if we need to update the master auto-approval state
// Create a new toggles state with the updated value
const updatedToggles = {
...toggles,
[key]: value,
}
const willHaveEnabledOptions = Object.values(updatedToggles).some((v) => !!v)
// If enabling the first option, enable master auto-approval
if (value && !hasEnabledOptions && willHaveEnabledOptions) {
setAutoApprovalEnabled(true)
vscode.postMessage({ type: "autoApprovalEnabled", bool: true })
}
// If disabling the last option, disable master auto-approval
else if (!value && hasEnabledOptions && !willHaveEnabledOptions) {
setAutoApprovalEnabled(false)
vscode.postMessage({ type: "autoApprovalEnabled", bool: false })
}
},
[
toggles,
hasEnabledOptions,
setAlwaysAllowReadOnly,
setAlwaysAllowWrite,
setAlwaysAllowExecute,
@ -124,7 +101,6 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
setAlwaysApproveResubmit,
setAlwaysAllowFollowupQuestions,
setAlwaysAllowUpdateTodoList,
setAutoApprovalEnabled,
],
)
@ -170,14 +146,14 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
const trigger = (
<PopoverTrigger
className={cn(
"inline-flex items-center gap-1.5 relative whitespace-nowrap px-2 py-1 text-xs",
"inline-flex items-center gap-1.5 relative whitespace-nowrap px-2 py-1 text-xs flex-shrink-1",
"bg-transparent border border-[rgba(255,255,255,0.08)] rounded-md text-vscode-foreground",
"transition-all duration-150 focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder focus-visible:ring-inset",
"opacity-90 hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)] cursor-pointer",
)}
style={style}>
<Stamp className="size-3.5 opacity-80 flex-shrink-0" />
<span className="font-medium">{t("chat:autoApprove.title")}</span>
<span className="font-medium">{t("chat:autoApprove.dropdownTitle")}</span>
<span className="text-vscode-descriptionForeground truncate max-w-[200px]">{displayText}</span>
</PopoverTrigger>
)
@ -192,50 +168,11 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
container={portalContainer}
className="p-0 overflow-hidden min-w-[400px] max-w-[500px]">
<div className="flex flex-col w-full">
{/* Header with master toggle */}
<div className="flex items-center justify-between p-3 border-b border-vscode-dropdown-border">
<div className="flex items-center gap-2">
<StandardTooltip
content={!hasEnabledOptions ? t("chat:autoApprove.selectOptionsFirst") : undefined}>
<VSCodeCheckbox
checked={effectiveAutoApprovalEnabled}
disabled={!hasEnabledOptions}
aria-label={
hasEnabledOptions
? t("chat:autoApprove.toggleAriaLabel")
: t("chat:autoApprove.disabledAriaLabel")
}
onChange={() => {
if (hasEnabledOptions) {
const newValue = !(autoApprovalEnabled ?? false)
setAutoApprovalEnabled(newValue)
vscode.postMessage({ type: "autoApprovalEnabled", bool: newValue })
}
}}
/>
</StandardTooltip>
<h4 className="m-0 font-medium text-sm">{t("chat:autoApprove.title")}</h4>
</div>
<div className="flex items-center gap-1">
<StandardTooltip content={t("chat:autoApprove.selectAll")}>
<button
onClick={handleSelectAll}
className="p-1 rounded hover:bg-vscode-list-hoverBackground transition-colors">
<ListChecks className="size-4" />
</button>
</StandardTooltip>
<StandardTooltip content={t("chat:autoApprove.selectNone")}>
<button
onClick={handleSelectNone}
className="p-1 rounded hover:bg-vscode-list-hoverBackground transition-colors">
<LayoutList className="size-4" />
</button>
</StandardTooltip>
</div>
</div>
{/* Description */}
<div className="px-3 py-2 border-b border-vscode-dropdown-border">
{/* Header */}
<div className="p-3 border-b border-vscode-dropdown-border">
<h3 className="m-0 font-bold text-sm text-vscode-button-foreground pb-2">
{t("chat:autoApprove.title")}
</h3>
<div
style={{
color: "var(--vscode-descriptionForeground)",
@ -256,6 +193,24 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
<AutoApproveToggleDropdown {...toggles} onToggle={onAutoApproveToggle} />
</div>
</div>
{/* Footer with buttons on left and title on right */}
<div className="flex items-center justify-between px-3 py-2 border-t border-vscode-dropdown-border">
<div className="flex items-center gap-2">
<button
onClick={handleSelectAll}
className="inline-flex items-center gap-1.5 px-3 py-1 text-xs rounded border border-vscode-button-border bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground hover:bg-vscode-button-secondaryHoverBackground transition-colors cursor-pointer">
<ListChecks className="size-3.5" />
All
</button>
<button
onClick={handleSelectNone}
className="inline-flex items-center gap-1.5 px-3 py-1 text-xs rounded border border-vscode-button-border bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground hover:bg-vscode-button-secondaryHoverBackground transition-colors cursor-pointer">
<LayoutList className="size-3.5" />
None
</button>
</div>
</div>
</div>
</PopoverContent>
</Popover>

View file

@ -56,20 +56,22 @@ export const AutoApproveToggleDropdown = ({ onToggle, ...props }: AutoApproveTog
data-testid={testId}
className={cn(
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-xs text-left",
"transition-colors hover:bg-vscode-list-hoverBackground",
"transition-colors hover:bg-vscode-list-hoverBackground cursor-pointer",
props[key]
? "bg-vscode-list-activeSelectionBackground text-vscode-list-activeSelectionForeground"
: "opacity-70",
)}>
<span className={cn("codicon", `codicon-${icon}`, "text-sm flex-shrink-0")} />
<span className="flex-1 truncate">{t(labelKey)}</span>
<span
className={cn(
"text-[10px] px-1 rounded",
props[key] ? "bg-vscode-badge-background text-vscode-badge-foreground" : "",
)}>
{props[key] ? "✓" : ""}
</span>
{DEFAULT_KEYBOARD_CONFIG.enabled && (
<span
className={cn(
"text-[10px] px-1 rounded",
"bg-vscode-badge-background text-vscode-badge-foreground opacity-60",
)}>
{shortcutDisplay.replace("Alt+", "⌥").replace("Ctrl+Shift+", "⌃⇧")}
</span>
)}
</button>
</StandardTooltip>
)

View file

@ -32,6 +32,7 @@ import { SlashCommandsPopover } from "./SlashCommandsPopover"
import { cn } from "@/lib/utils"
import { usePromptHistory } from "./hooks/usePromptHistory"
import { EditModeControls } from "./EditModeControls"
import AutoApproveMenu from "./AutoApproveMenu"
interface ChatTextAreaProps {
inputValue: string
@ -917,31 +918,58 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
// Helper function to render non-edit mode controls
const renderNonEditModeControls = () => (
<div className={cn("flex", "justify-between", "items-center", "mt-auto")}>
<div className={cn("flex", "items-center", "gap-1", "min-w-0")}>
<div className="shrink-0">{renderModeSelector()}</div>
<div className={cn("flex", "flex-col", "gap-1", "mt-auto")}>
<div className={cn("flex", "justify-between", "items-center")}>
<div className={cn("flex", "items-center", "gap-1", "min-w-0", "flex-nowrap")}>
<div>{renderModeSelector()}</div>
<div className={cn("flex-1", "min-w-0", "overflow-hidden")}>
<ApiConfigSelector
value={currentConfigId}
displayName={displayName}
disabled={selectApiConfigDisabled}
title={t("chat:selectApiConfig")}
onChange={handleApiConfigChange}
triggerClassName="w-full text-ellipsis overflow-hidden"
listApiConfigMeta={listApiConfigMeta || []}
pinnedApiConfigs={pinnedApiConfigs}
togglePinnedApiConfig={togglePinnedApiConfig}
/>
<div>
<ApiConfigSelector
value={currentConfigId}
displayName={displayName}
disabled={selectApiConfigDisabled}
title={t("chat:selectApiConfig")}
onChange={handleApiConfigChange}
triggerClassName="w-full text-ellipsis overflow-hidden"
listApiConfigMeta={listApiConfigMeta || []}
pinnedApiConfigs={pinnedApiConfigs}
togglePinnedApiConfig={togglePinnedApiConfig}
/>
</div>
<div className="grow-1 shrink max-w-100% overflow-hidden">
<AutoApproveMenu />
</div>
</div>
</div>
<div className={cn("flex", "items-center", "gap-0.5", "shrink-0")}>
{isTtsPlaying && (
<StandardTooltip content={t("chat:stopTts")}>
<div className={cn("flex", "items-center", "gap-0.5", "shrink-0")}>
{isTtsPlaying && (
<StandardTooltip content={t("chat:stopTts")}>
<button
aria-label={t("chat:stopTts")}
onClick={() => vscode.postMessage({ type: "stopTts" })}
className={cn(
"relative inline-flex items-center justify-center",
"bg-transparent border-none p-1.5",
"rounded-md min-w-[28px] min-h-[28px]",
"text-vscode-foreground opacity-85",
"transition-all duration-150",
"hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
"cursor-pointer",
)}>
<VolumeX className="w-4 h-4" />
</button>
</StandardTooltip>
)}
<SlashCommandsPopover />
<IndexingStatusBadge />
<StandardTooltip content={t("chat:addImages")}>
<button
aria-label={t("chat:stopTts")}
onClick={() => vscode.postMessage({ type: "stopTts" })}
aria-label={t("chat:addImages")}
disabled={shouldDisableImages}
onClick={!shouldDisableImages ? onSelectImages : undefined}
className={cn(
"relative inline-flex items-center justify-center",
"bg-transparent border-none p-1.5",
@ -951,36 +979,15 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
"hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
"cursor-pointer",
!shouldDisableImages && "cursor-pointer",
shouldDisableImages &&
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
"mr-1",
)}>
<VolumeX className="w-4 h-4" />
<Image className="w-4 h-4" />
</button>
</StandardTooltip>
)}
<SlashCommandsPopover />
<IndexingStatusBadge />
<StandardTooltip content={t("chat:addImages")}>
<button
aria-label={t("chat:addImages")}
disabled={shouldDisableImages}
onClick={!shouldDisableImages ? onSelectImages : undefined}
className={cn(
"relative inline-flex items-center justify-center",
"bg-transparent border-none p-1.5",
"rounded-md min-w-[28px] min-h-[28px]",
"text-vscode-foreground opacity-85",
"transition-all duration-150",
"hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
!shouldDisableImages && "cursor-pointer",
shouldDisableImages &&
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
"mr-1",
)}>
<Image className="w-4 h-4" />
</button>
</StandardTooltip>
</div>
</div>
</div>
)

View file

@ -50,7 +50,6 @@ import BrowserSessionRow from "./BrowserSessionRow"
import ChatRow from "./ChatRow"
import ChatTextArea from "./ChatTextArea"
import TaskHeader from "./TaskHeader"
import AutoApproveMenu from "./AutoApproveMenu"
import { AutoApproveKeyboardShortcuts } from "./AutoApproveKeyboardShortcuts"
import SystemPromptWarning from "./SystemPromptWarning"
import ProfileViolationWarning from "./ProfileViolationWarning"
@ -2019,11 +2018,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
modeShortcutText={modeShortcutText}
/>
{/* Auto-Approve Menu positioned below text area */}
<div className="px-3 pb-2">
<AutoApproveMenu />
</div>
{isProfileDisabled && (
<div className="px-3">
<ProfileViolationWarning />

View file

@ -265,9 +265,10 @@
"issues": "It seems like you're having Windows PowerShell issues, please see this"
},
"autoApprove": {
"title": "Auto-approve:",
"title": "Auto-Approve",
"dropdownTitle": "Auto:",
"none": "None",
"description": "Auto-approve allows Roo Code to perform actions without asking for permission. Only enable for actions you fully trust. More detailed configuration available in <settingsLink>Settings</settingsLink>.",
"description": "Run these actions without asking for permission.Only enable for actions you fully trust. More in <settingsLink>Settings</settingsLink>.",
"selectOptionsFirst": "Select at least one option below to enable auto-approval",
"toggleAriaLabel": "Toggle auto-approval",
"disabledAriaLabel": "Auto-approval disabled - select options first"