import { VSCodeButton, VSCodeCheckbox, VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { memo, useEffect, useState } from "react" import { useExtensionState } from "../../context/ExtensionStateContext" import { validateApiConfiguration, validateModelId } from "../../utils/validate" import { vscode } from "../../utils/vscode" import ApiOptions from "./ApiOptions" import ExperimentalFeature from "./ExperimentalFeature" import { EXPERIMENT_IDS, experimentConfigsMap } from "../../../../src/shared/experiments" import ApiConfigManager from "./ApiConfigManager" import { Dropdown } from "vscrui" import type { DropdownOption } from "vscrui" type SettingsViewProps = { onDone: () => void } const SettingsView = ({ onDone }: SettingsViewProps) => { const { apiConfiguration, version, alwaysAllowReadOnly, setAlwaysAllowReadOnly, alwaysAllowWrite, setAlwaysAllowWrite, alwaysAllowExecute, setAlwaysAllowExecute, alwaysAllowBrowser, setAlwaysAllowBrowser, alwaysAllowMcp, setAlwaysAllowMcp, soundEnabled, setSoundEnabled, soundVolume, setSoundVolume, diffEnabled, setDiffEnabled, browserViewportSize, setBrowserViewportSize, openRouterModels, glamaModels, setAllowedCommands, allowedCommands, fuzzyMatchThreshold, setFuzzyMatchThreshold, writeDelayMs, setWriteDelayMs, screenshotQuality, setScreenshotQuality, terminalOutputLineLimit, setTerminalOutputLineLimit, mcpEnabled, alwaysApproveResubmit, setAlwaysApproveResubmit, requestDelaySeconds, setRequestDelaySeconds, rateLimitSeconds, setRateLimitSeconds, currentApiConfigName, listApiConfigMeta, experiments, setExperimentEnabled, alwaysAllowModeSwitch, setAlwaysAllowModeSwitch, } = useExtensionState() const [apiErrorMessage, setApiErrorMessage] = useState(undefined) const [modelIdErrorMessage, setModelIdErrorMessage] = useState(undefined) const [commandInput, setCommandInput] = useState("") const handleSubmit = () => { const apiValidationResult = validateApiConfiguration(apiConfiguration) const modelIdValidationResult = validateModelId(apiConfiguration, glamaModels, openRouterModels) setApiErrorMessage(apiValidationResult) setModelIdErrorMessage(modelIdValidationResult) if (!apiValidationResult && !modelIdValidationResult) { vscode.postMessage({ type: "apiConfiguration", apiConfiguration, }) vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly }) vscode.postMessage({ type: "alwaysAllowWrite", bool: alwaysAllowWrite }) vscode.postMessage({ type: "alwaysAllowExecute", bool: alwaysAllowExecute }) vscode.postMessage({ type: "alwaysAllowBrowser", bool: alwaysAllowBrowser }) vscode.postMessage({ type: "alwaysAllowMcp", bool: alwaysAllowMcp }) vscode.postMessage({ type: "allowedCommands", commands: allowedCommands ?? [] }) vscode.postMessage({ type: "soundEnabled", bool: soundEnabled }) vscode.postMessage({ type: "soundVolume", value: soundVolume }) vscode.postMessage({ type: "diffEnabled", bool: diffEnabled }) vscode.postMessage({ type: "browserViewportSize", text: browserViewportSize }) vscode.postMessage({ type: "fuzzyMatchThreshold", value: fuzzyMatchThreshold ?? 1.0 }) vscode.postMessage({ type: "writeDelayMs", value: writeDelayMs }) vscode.postMessage({ type: "screenshotQuality", value: screenshotQuality ?? 75 }) vscode.postMessage({ type: "terminalOutputLineLimit", value: terminalOutputLineLimit ?? 500 }) vscode.postMessage({ type: "mcpEnabled", bool: mcpEnabled }) vscode.postMessage({ type: "alwaysApproveResubmit", bool: alwaysApproveResubmit }) vscode.postMessage({ type: "requestDelaySeconds", value: requestDelaySeconds }) vscode.postMessage({ type: "rateLimitSeconds", value: rateLimitSeconds }) vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName }) vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration, }) vscode.postMessage({ type: "updateExperimental", values: experiments, }) vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch }) onDone() } } useEffect(() => { setApiErrorMessage(undefined) setModelIdErrorMessage(undefined) }, [apiConfiguration]) // Initial validation on mount useEffect(() => { const apiValidationResult = validateApiConfiguration(apiConfiguration) const modelIdValidationResult = validateModelId(apiConfiguration, glamaModels, openRouterModels) setApiErrorMessage(apiValidationResult) setModelIdErrorMessage(modelIdValidationResult) }, [apiConfiguration, glamaModels, openRouterModels]) const handleResetState = () => { vscode.postMessage({ type: "resetState" }) } const handleAddCommand = () => { const currentCommands = allowedCommands ?? [] if (commandInput && !currentCommands.includes(commandInput)) { const newCommands = [...currentCommands, commandInput] setAllowedCommands(newCommands) setCommandInput("") vscode.postMessage({ type: "allowedCommands", commands: newCommands, }) } } const sliderLabelStyle = { minWidth: "45px", textAlign: "right" as const, lineHeight: "20px", paddingBottom: "2px", } const sliderStyle = { flexGrow: 1, maxWidth: "80%", accentColor: "var(--vscode-button-background)", height: "2px", } return (

Settings

Done

Provider Settings

{ vscode.postMessage({ type: "loadApiConfiguration", text: configName, }) }} onDeleteConfig={(configName: string) => { vscode.postMessage({ type: "deleteApiConfiguration", text: configName, }) }} onRenameConfig={(oldName: string, newName: string) => { vscode.postMessage({ type: "renameApiConfiguration", values: { oldName, newName }, apiConfiguration, }) }} onUpsertConfig={(configName: string) => { vscode.postMessage({ type: "upsertApiConfiguration", text: configName, apiConfiguration, }) }} />

Auto-Approve Settings

The following settings allow Roo to automatically perform operations without requiring approval. Enable these settings only if you fully trust the AI and understand the associated security risks.

setAlwaysAllowReadOnly(e.target.checked)}> Always approve read-only operations

When enabled, Roo will automatically view directory contents and read files without requiring you to click the Approve button.

setAlwaysAllowWrite(e.target.checked)}> Always approve write operations

Automatically create and edit files without requiring approval

{alwaysAllowWrite && (
setWriteDelayMs(parseInt(e.target.value))} style={{ flex: 1, accentColor: "var(--vscode-button-background)", height: "2px", }} /> {writeDelayMs}ms

Delay after writes to allow diagnostics to detect potential problems

)}
setAlwaysAllowBrowser(e.target.checked)}> Always approve browser actions

Automatically perform browser actions without requiring approval
Note: Only applies when the model supports computer use

setAlwaysApproveResubmit(e.target.checked)}> Always retry failed API requests

Automatically retry failed API requests when server returns an error response

{alwaysApproveResubmit && (
setRequestDelaySeconds(parseInt(e.target.value))} style={{ flex: 1, accentColor: "var(--vscode-button-background)", height: "2px", }} /> {requestDelaySeconds}s

Delay before retrying the request

)}
setAlwaysAllowMcp(e.target.checked)}> Always approve MCP tools

Enable auto-approval of individual MCP tools in the MCP Servers view (requires both this setting and the tool's individual "Always allow" checkbox)

setAlwaysAllowModeSwitch(e.target.checked)}> Always approve mode switching & task creation

Automatically switch between different AI modes and create new tasks without requiring approval

setAlwaysAllowExecute(e.target.checked)}> Always approve allowed execute operations

Automatically execute allowed terminal commands without requiring approval

{alwaysAllowExecute && (
Allowed Auto-Execute Commands

Command prefixes that can be auto-executed when "Always approve execute operations" is enabled.

setCommandInput(e.target.value)} onKeyDown={(e: any) => { if (e.key === "Enter") { e.preventDefault() handleAddCommand() } }} placeholder="Enter command prefix (e.g., 'git ')" style={{ flexGrow: 1 }} /> Add
{(allowedCommands ?? []).map((cmd, index) => (
{cmd} { const newCommands = (allowedCommands ?? []).filter( (_, i) => i !== index, ) setAllowedCommands(newCommands) vscode.postMessage({ type: "allowedCommands", commands: newCommands, }) }}>
))}
)}

Browser Settings

{ setBrowserViewportSize((value as DropdownOption).value) }} style={{ width: "100%" }} options={[ { value: "1280x800", label: "Large Desktop (1280x800)" }, { value: "900x600", label: "Small Desktop (900x600)" }, { value: "768x1024", label: "Tablet (768x1024)" }, { value: "360x640", label: "Mobile (360x640)" }, ]} />

Select the viewport size for browser interactions. This affects how websites are displayed and interacted with.

Screenshot quality
setScreenshotQuality(parseInt(e.target.value))} style={{ ...sliderStyle, }} /> {screenshotQuality ?? 75}%

Adjust the WebP quality of browser screenshots. Higher values provide clearer screenshots but increase token usage.

Notification Settings

setSoundEnabled(e.target.checked)}> Enable sound effects

When enabled, Roo will play sound effects for notifications and events.

{soundEnabled && (
Volume setSoundVolume(parseFloat(e.target.value))} style={{ flexGrow: 1, accentColor: "var(--vscode-button-background)", height: "2px", }} aria-label="Volume" /> {((soundVolume ?? 0.5) * 100).toFixed(0)}%
)}

Advanced Settings

Rate limit
setRateLimitSeconds(parseInt(e.target.value))} style={{ ...sliderStyle }} /> {rateLimitSeconds}s

Minimum time between API requests.

Terminal output limit
setTerminalOutputLineLimit(parseInt(e.target.value))} style={{ ...sliderStyle }} /> {terminalOutputLineLimit ?? 500}

Maximum number of lines to include in terminal output when executing commands. When exceeded lines will be removed from the middle, saving tokens.

{ setDiffEnabled(e.target.checked) if (!e.target.checked) { // Reset experimental strategy when diffs are disabled setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY, false) } }}> Enable editing through diffs

When enabled, Roo will be able to edit files more quickly and will automatically reject truncated full-file writes. Works best with the latest Claude 3.5 Sonnet model.

{diffEnabled && (
setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY, enabled)} />
Match precision
{ setFuzzyMatchThreshold(parseFloat(e.target.value)) }} style={{ ...sliderStyle, }} /> {Math.round((fuzzyMatchThreshold || 1) * 100)}%

This slider controls how precisely code sections must match when applying diffs. Lower values allow more flexible matching but increase the risk of incorrect replacements. Use values below 100% with extreme caution.

)} {Object.entries(experimentConfigsMap) .filter((config) => config[0] !== "DIFF_STRATEGY") .map((config) => ( setExperimentEnabled( EXPERIMENT_IDS[config[0] as keyof typeof EXPERIMENT_IDS], enabled, ) } /> ))}

If you have any questions or feedback, feel free to open an issue at{" "} github.com/RooVetGit/Roo-Code {" "} or join{" "} reddit.com/r/RooCode

v{version}

This will reset all global state and secret storage in the extension.

Reset State
) } export default memo(SettingsView)