Browse Source

Feat: Vertical settings tabs (#2914)

Co-authored-by: Matt Rubens <[email protected]>
dlab-anton 8 months ago
parent
commit
c701600081

+ 21 - 3
src/activate/registerCommands.ts

@@ -116,6 +116,8 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
 			telemetryService.captureTitleButtonClicked("settings")
 
 			visibleProvider.postMessageToWebview({ type: "action", action: "settingsButtonClicked" })
+			// Also explicitly post the visibility message to trigger scroll reliably
+			visibleProvider.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
 		},
 		"roo-cline.historyButtonClicked": () => {
 			const visibleProvider = getVisibleProviderOrLog(outputChannel)
@@ -212,10 +214,26 @@ export const openClineInNewTab = async ({ context, outputChannel }: Omit<Registe
 
 	await tabProvider.resolveWebviewView(newPanel)
 
+	// Add listener for visibility changes to notify webview
+	newPanel.onDidChangeViewState(
+		(e) => {
+			const panel = e.webviewPanel
+			if (panel.visible) {
+				panel.webview.postMessage({ type: "action", action: "didBecomeVisible" }) // Use the same message type as in SettingsView.tsx
+			}
+		},
+		null, // First null is for `thisArgs`
+		context.subscriptions, // Register listener for disposal
+	)
+
 	// Handle panel closing events.
-	newPanel.onDidDispose(() => {
-		setPanel(undefined, "tab")
-	})
+	newPanel.onDidDispose(
+		() => {
+			setPanel(undefined, "tab")
+		},
+		null,
+		context.subscriptions, // Also register dispose listener
+	)
 
 	// Lock the editor group so clicking on files doesn't open them over the panel.
 	await delay(100)

+ 46 - 2
webview-ui/src/components/common/Tab.tsx

@@ -1,4 +1,4 @@
-import { HTMLAttributes, useCallback } from "react"
+import React, { HTMLAttributes, useCallback, forwardRef } from "react"
 
 import { useExtensionState } from "@/context/ExtensionStateContext"
 import { cn } from "@/lib/utils"
@@ -6,7 +6,7 @@ import { cn } from "@/lib/utils"
 type TabProps = HTMLAttributes<HTMLDivElement>
 
 export const Tab = ({ className, children, ...props }: TabProps) => (
-	<div className={cn("fixed inset-0 flex flex-col overflow-hidden", className)} {...props}>
+	<div className={cn("fixed inset-0 flex flex-col", className)} {...props}>
 		{children}
 	</div>
 )
@@ -45,3 +45,47 @@ export const TabContent = ({ className, children, ...props }: TabProps) => {
 		</div>
 	)
 }
+
+export const TabList = forwardRef<
+	HTMLDivElement,
+	HTMLAttributes<HTMLDivElement> & {
+		value: string
+		onValueChange: (value: string) => void
+	}
+>(({ children, className, value, onValueChange, ...props }, ref) => {
+	return (
+		<div ref={ref} role="tablist" className={cn("flex", className)} {...props}>
+			{React.Children.map(children, (child) => {
+				if (React.isValidElement(child)) {
+					return React.cloneElement(child as React.ReactElement<any>, {
+						isSelected: child.props.value === value,
+						onSelect: () => onValueChange(child.props.value),
+					})
+				}
+				return child
+			})}
+		</div>
+	)
+})
+
+export const TabTrigger = forwardRef<
+	HTMLButtonElement,
+	React.ButtonHTMLAttributes<HTMLButtonElement> & {
+		value: string
+		isSelected?: boolean
+		onSelect?: () => void
+	}
+>(({ children, className, value: _value, isSelected, onSelect, ...props }, ref) => {
+	return (
+		<button
+			ref={ref}
+			role="tab"
+			aria-selected={isSelected}
+			tabIndex={isSelected ? 0 : -1}
+			className={cn("focus:outline-none focus:ring-2 focus:ring-vscode-focusBorder", className)}
+			onClick={onSelect}
+			{...props}>
+			{children}
+		</button>
+	)
+})

+ 327 - 199
webview-ui/src/components/settings/SettingsView.tsx

@@ -1,4 +1,14 @@
-import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"
+import React, {
+	forwardRef,
+	memo,
+	useCallback,
+	useEffect,
+	useImperativeHandle,
+	useLayoutEffect,
+	useMemo,
+	useRef,
+	useState,
+} from "react"
 import { useAppTranslation } from "@/i18n/TranslationContext"
 import {
 	CheckCheck,
@@ -14,7 +24,6 @@ import {
 	Info,
 	LucideIcon,
 } from "lucide-react"
-import { CaretSortIcon } from "@radix-ui/react-icons"
 
 import { ExperimentId } from "@roo/shared/experiments"
 import { TelemetrySetting } from "@roo/shared/TelemetrySetting"
@@ -32,13 +41,13 @@ import {
 	AlertDialogHeader,
 	AlertDialogFooter,
 	Button,
-	DropdownMenu,
-	DropdownMenuTrigger,
-	DropdownMenuContent,
-	DropdownMenuItem,
+	Tooltip,
+	TooltipContent,
+	TooltipProvider,
+	TooltipTrigger,
 } from "@/components/ui"
 
-import { Tab, TabContent, TabHeader } from "../common/Tab"
+import { Tab, TabContent, TabHeader, TabList, TabTrigger } from "../common/Tab"
 import { SetCachedStateField, SetExperimentEnabled } from "./types"
 import { SectionHeader } from "./SectionHeader"
 import ApiConfigManager from "./ApiConfigManager"
@@ -53,6 +62,14 @@ import { ExperimentalSettings } from "./ExperimentalSettings"
 import { LanguageSettings } from "./LanguageSettings"
 import { About } from "./About"
 import { Section } from "./Section"
+import { cn } from "@/lib/utils"
+
+export const settingsTabsContainer = "flex flex-1 overflow-hidden [&.narrow_.tab-label]:hidden"
+export const settingsTabList =
+	"w-48 data-[compact=true]:w-12 flex-shrink-0 flex flex-col overflow-y-auto overflow-x-hidden border-r border-vscode-sideBar-background"
+export const settingsTabTrigger =
+	"whitespace-nowrap overflow-hidden min-w-0 h-12 px-4 py-3 box-border flex items-center border-l-2 border-transparent text-vscode-foreground opacity-70 hover:bg-vscode-list-hoverBackground data-[compact=true]:w-12 data-[compact=true]:p-4"
+export const settingsTabTriggerActive = "opacity-100 border-vscode-focusBorder bg-vscode-list-activeSelectionBackground"
 
 export interface SettingsViewRef {
 	checkUnsaveChanges: (then: () => void) => void
@@ -87,6 +104,11 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 	const [isDiscardDialogShow, setDiscardDialogShow] = useState(false)
 	const [isChangeDetected, setChangeDetected] = useState(false)
 	const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
+	const [activeTab, setActiveTab] = useState<SectionName>(
+		targetSection && sectionNames.includes(targetSection as SectionName)
+			? (targetSection as SectionName)
+			: "providers",
+	)
 
 	const prevApiConfigName = useRef(currentApiConfigName)
 	const confirmDialogHandler = useRef<() => void>()
@@ -125,7 +147,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 		telemetrySetting,
 		terminalOutputLineLimit,
 		terminalShellIntegrationTimeout,
-		terminalShellIntegrationDisabled,
+		terminalShellIntegrationDisabled, // Added from upstream
 		terminalCommandDelay,
 		terminalPowershellCounter,
 		terminalZshClearEolMark,
@@ -139,7 +161,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 		terminalCompressProgressBar,
 	} = cachedState
 
-	// Make sure apiConfiguration is initialized and managed by SettingsView.
 	const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration])
 
 	useEffect(() => {
@@ -279,81 +300,116 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 
 	useImperativeHandle(ref, () => ({ checkUnsaveChanges }), [checkUnsaveChanges])
 
-	const onConfirmDialogResult = useCallback((confirm: boolean) => {
-		if (confirm) {
-			confirmDialogHandler.current?.()
+	const onConfirmDialogResult = useCallback(
+		(confirm: boolean) => {
+			if (confirm) {
+				// Discard changes: Reset state and flag
+				setCachedState(extensionState) // Revert to original state
+				setChangeDetected(false) // Reset change flag
+				confirmDialogHandler.current?.() // Execute the pending action (e.g., tab switch)
+			}
+			// If confirm is false (Cancel), do nothing, dialog closes automatically
+		},
+		[extensionState], // Depend on extensionState to get the latest original state
+	)
+
+	// Handle tab changes with unsaved changes check
+	const handleTabChange = useCallback(
+		(newTab: SectionName) => {
+			// Directly switch tab without checking for unsaved changes
+			setActiveTab(newTab)
+		},
+		[], // No dependency on isChangeDetected needed anymore
+	)
+
+	// Store direct DOM element refs for each tab
+	const tabRefs = useRef<Record<SectionName, HTMLButtonElement | null>>(
+		Object.fromEntries(sectionNames.map((name) => [name, null])) as Record<SectionName, HTMLButtonElement | null>,
+	)
+
+	// Track whether we're in compact mode
+	const [isCompactMode, setIsCompactMode] = useState(false)
+	const containerRef = useRef<HTMLDivElement>(null)
+
+	// Setup resize observer to detect when we should switch to compact mode
+	useEffect(() => {
+		if (!containerRef.current) return
+
+		const observer = new ResizeObserver((entries) => {
+			for (const entry of entries) {
+				// If container width is less than 500px, switch to compact mode
+				setIsCompactMode(entry.contentRect.width < 500)
+			}
+		})
+
+		observer.observe(containerRef.current)
+
+		return () => {
+			observer?.disconnect()
 		}
 	}, [])
 
-	const providersRef = useRef<HTMLDivElement>(null)
-	const autoApproveRef = useRef<HTMLDivElement>(null)
-	const browserRef = useRef<HTMLDivElement>(null)
-	const checkpointsRef = useRef<HTMLDivElement>(null)
-	const notificationsRef = useRef<HTMLDivElement>(null)
-	const contextManagementRef = useRef<HTMLDivElement>(null)
-	const terminalRef = useRef<HTMLDivElement>(null)
-	const experimentalRef = useRef<HTMLDivElement>(null)
-	const languageRef = useRef<HTMLDivElement>(null)
-	const aboutRef = useRef<HTMLDivElement>(null)
-
-	const sections: { id: SectionName; icon: LucideIcon; ref: React.RefObject<HTMLDivElement> }[] = useMemo(
+	const sections: { id: SectionName; icon: LucideIcon }[] = useMemo(
 		() => [
-			{ id: "providers", icon: Webhook, ref: providersRef },
-			{ id: "autoApprove", icon: CheckCheck, ref: autoApproveRef },
-			{ id: "browser", icon: SquareMousePointer, ref: browserRef },
-			{ id: "checkpoints", icon: GitBranch, ref: checkpointsRef },
-			{ id: "notifications", icon: Bell, ref: notificationsRef },
-			{ id: "contextManagement", icon: Database, ref: contextManagementRef },
-			{ id: "terminal", icon: SquareTerminal, ref: terminalRef },
-			{ id: "experimental", icon: FlaskConical, ref: experimentalRef },
-			{ id: "language", icon: Globe, ref: languageRef },
-			{ id: "about", icon: Info, ref: aboutRef },
-		],
-		[
-			providersRef,
-			autoApproveRef,
-			browserRef,
-			checkpointsRef,
-			notificationsRef,
-			contextManagementRef,
-			terminalRef,
-			experimentalRef,
+			{ id: "providers", icon: Webhook },
+			{ id: "autoApprove", icon: CheckCheck },
+			{ id: "browser", icon: SquareMousePointer },
+			{ id: "checkpoints", icon: GitBranch },
+			{ id: "notifications", icon: Bell },
+			{ id: "contextManagement", icon: Database },
+			{ id: "terminal", icon: SquareTerminal },
+			{ id: "experimental", icon: FlaskConical },
+			{ id: "language", icon: Globe },
+			{ id: "about", icon: Info },
 		],
+		[], // No dependencies needed now
 	)
 
-	const scrollToSection = (ref: React.RefObject<HTMLDivElement>) => ref.current?.scrollIntoView()
+	// Update target section logic to set active tab
+	useEffect(() => {
+		if (targetSection && sectionNames.includes(targetSection as SectionName)) {
+			setActiveTab(targetSection as SectionName)
+		}
+	}, [targetSection])
+
+	// Function to scroll the active tab into view for vertical layout
+	const scrollToActiveTab = useCallback(() => {
+		const activeTabElement = tabRefs.current[activeTab]
+
+		if (activeTabElement) {
+			activeTabElement.scrollIntoView({
+				behavior: "auto",
+				block: "nearest",
+			})
+		}
+	}, [activeTab])
 
-	// Scroll to target section when specified
+	// Effect to scroll when the active tab changes
 	useEffect(() => {
-		if (targetSection) {
-			const sectionObj = sections.find((section) => section.id === targetSection)
-			if (sectionObj && sectionObj.ref.current) {
-				// Use setTimeout to ensure the scroll happens after render
-				setTimeout(() => scrollToSection(sectionObj.ref), 500)
+		scrollToActiveTab()
+	}, [activeTab, scrollToActiveTab])
+
+	// Effect to scroll when the webview becomes visible
+	useLayoutEffect(() => {
+		const handleMessage = (event: MessageEvent) => {
+			const message = event.data
+			if (message.type === "action" && message.action === "didBecomeVisible") {
+				scrollToActiveTab()
 			}
 		}
-	}, [targetSection, sections])
+
+		window.addEventListener("message", handleMessage)
+
+		return () => {
+			window.removeEventListener("message", handleMessage)
+		}
+	}, [scrollToActiveTab])
 
 	return (
 		<Tab>
 			<TabHeader className="flex justify-between items-center gap-2">
 				<div className="flex items-center gap-1">
 					<h3 className="text-vscode-foreground m-0">{t("settings:header.title")}</h3>
-					<DropdownMenu>
-						<DropdownMenuTrigger asChild>
-							<Button variant="ghost" size="icon" className="w-6 h-6">
-								<CaretSortIcon />
-							</Button>
-						</DropdownMenuTrigger>
-						<DropdownMenuContent align="start" side="bottom">
-							{sections.map(({ id, icon: Icon, ref }) => (
-								<DropdownMenuItem key={id} onClick={() => scrollToSection(ref)}>
-									<Icon />
-									<span>{t(`settings:sections.${id}`)}</span>
-								</DropdownMenuItem>
-							))}
-						</DropdownMenuContent>
-					</DropdownMenu>
 				</div>
 				<div className="flex gap-2">
 					<Button
@@ -380,142 +436,214 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 				</div>
 			</TabHeader>
 
-			<TabContent className="p-0 divide-y divide-vscode-sideBar-background">
-				<div ref={providersRef}>
-					<SectionHeader>
-						<div className="flex items-center gap-2">
-							<Webhook className="w-4" />
-							<div>{t("settings:sections.providers")}</div>
+			{/* Vertical tabs layout */}
+			<div ref={containerRef} className={cn(settingsTabsContainer, isCompactMode && "narrow")}>
+				{/* Tab sidebar */}
+				<TabList
+					value={activeTab}
+					onValueChange={(value) => handleTabChange(value as SectionName)}
+					className={cn(settingsTabList)}
+					data-compact={isCompactMode}
+					data-testid="settings-tab-list">
+					{sections.map(({ id, icon: Icon }) => {
+						const isSelected = id === activeTab
+						const onSelect = () => handleTabChange(id)
+
+						// Base TabTrigger component definition
+						// We pass isSelected manually for styling, but onSelect is handled conditionally
+						const triggerComponent = (
+							<TabTrigger
+								ref={(element) => (tabRefs.current[id] = element)}
+								value={id}
+								isSelected={isSelected} // Pass manually for styling state
+								className={cn(
+									isSelected // Use manual isSelected for styling
+										? `${settingsTabTrigger} ${settingsTabTriggerActive}`
+										: settingsTabTrigger,
+									"focus:ring-0", // Remove the focus ring styling
+								)}
+								data-testid={`tab-${id}`}
+								data-compact={isCompactMode}>
+								<div className={cn("flex items-center gap-2", isCompactMode && "justify-center")}>
+									<Icon className="w-4 h-4" />
+									<span className="tab-label">{t(`settings:sections.${id}`)}</span>
+								</div>
+							</TabTrigger>
+						)
+
+						if (isCompactMode) {
+							// Wrap in Tooltip and manually add onClick to the trigger
+							return (
+								<TooltipProvider key={id} delayDuration={0}>
+									<Tooltip>
+										<TooltipTrigger asChild onClick={onSelect}>
+											{/* Clone to avoid ref issues if triggerComponent itself had a key */}
+											{React.cloneElement(triggerComponent)}
+										</TooltipTrigger>
+										<TooltipContent side="right" className="text-base">
+											<p className="m-0">{t(`settings:sections.${id}`)}</p>
+										</TooltipContent>
+									</Tooltip>
+								</TooltipProvider>
+							)
+						} else {
+							// Render trigger directly; TabList will inject onSelect via cloning
+							// Ensure the element passed to TabList has the key
+							return React.cloneElement(triggerComponent, { key: id })
+						}
+					})}
+				</TabList>
+
+				{/* Content area */}
+				<TabContent className="p-0 flex-1 overflow-auto">
+					{/* Providers Section */}
+					{activeTab === "providers" && (
+						<div>
+							<SectionHeader>
+								<div className="flex items-center gap-2">
+									<Webhook className="w-4" />
+									<div>{t("settings:sections.providers")}</div>
+								</div>
+							</SectionHeader>
+
+							<Section>
+								<ApiConfigManager
+									currentApiConfigName={currentApiConfigName}
+									listApiConfigMeta={listApiConfigMeta}
+									onSelectConfig={(configName: string) =>
+										checkUnsaveChanges(() =>
+											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,
+										})
+										prevApiConfigName.current = newName
+									}}
+									onUpsertConfig={(configName: string) =>
+										vscode.postMessage({
+											type: "upsertApiConfiguration",
+											text: configName,
+											apiConfiguration,
+										})
+									}
+								/>
+								<ApiOptions
+									uriScheme={uriScheme}
+									apiConfiguration={apiConfiguration}
+									setApiConfigurationField={setApiConfigurationField}
+									errorMessage={errorMessage}
+									setErrorMessage={setErrorMessage}
+								/>
+							</Section>
 						</div>
-					</SectionHeader>
-
-					<Section>
-						<ApiConfigManager
-							currentApiConfigName={currentApiConfigName}
-							listApiConfigMeta={listApiConfigMeta}
-							onSelectConfig={(configName: string) =>
-								checkUnsaveChanges(() =>
-									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,
-								})
-								prevApiConfigName.current = newName
-							}}
-							onUpsertConfig={(configName: string) =>
-								vscode.postMessage({
-									type: "upsertApiConfiguration",
-									text: configName,
-									apiConfiguration,
-								})
-							}
+					)}
+
+					{/* Auto-Approve Section */}
+					{activeTab === "autoApprove" && (
+						<AutoApproveSettings
+							alwaysAllowReadOnly={alwaysAllowReadOnly}
+							alwaysAllowReadOnlyOutsideWorkspace={alwaysAllowReadOnlyOutsideWorkspace}
+							alwaysAllowWrite={alwaysAllowWrite}
+							alwaysAllowWriteOutsideWorkspace={alwaysAllowWriteOutsideWorkspace}
+							writeDelayMs={writeDelayMs}
+							alwaysAllowBrowser={alwaysAllowBrowser}
+							alwaysApproveResubmit={alwaysApproveResubmit}
+							requestDelaySeconds={requestDelaySeconds}
+							alwaysAllowMcp={alwaysAllowMcp}
+							alwaysAllowModeSwitch={alwaysAllowModeSwitch}
+							alwaysAllowSubtasks={alwaysAllowSubtasks}
+							alwaysAllowExecute={alwaysAllowExecute}
+							allowedCommands={allowedCommands}
+							setCachedStateField={setCachedStateField}
 						/>
-						<ApiOptions
-							uriScheme={uriScheme}
-							apiConfiguration={apiConfiguration}
-							setApiConfigurationField={setApiConfigurationField}
-							errorMessage={errorMessage}
-							setErrorMessage={setErrorMessage}
+					)}
+
+					{/* Browser Section */}
+					{activeTab === "browser" && (
+						<BrowserSettings
+							browserToolEnabled={browserToolEnabled}
+							browserViewportSize={browserViewportSize}
+							screenshotQuality={screenshotQuality}
+							remoteBrowserHost={remoteBrowserHost}
+							remoteBrowserEnabled={remoteBrowserEnabled}
+							setCachedStateField={setCachedStateField}
 						/>
-					</Section>
-				</div>
-
-				<div ref={autoApproveRef}>
-					<AutoApproveSettings
-						alwaysAllowReadOnly={alwaysAllowReadOnly}
-						alwaysAllowReadOnlyOutsideWorkspace={alwaysAllowReadOnlyOutsideWorkspace}
-						alwaysAllowWrite={alwaysAllowWrite}
-						alwaysAllowWriteOutsideWorkspace={alwaysAllowWriteOutsideWorkspace}
-						writeDelayMs={writeDelayMs}
-						alwaysAllowBrowser={alwaysAllowBrowser}
-						alwaysApproveResubmit={alwaysApproveResubmit}
-						requestDelaySeconds={requestDelaySeconds}
-						alwaysAllowMcp={alwaysAllowMcp}
-						alwaysAllowModeSwitch={alwaysAllowModeSwitch}
-						alwaysAllowSubtasks={alwaysAllowSubtasks}
-						alwaysAllowExecute={alwaysAllowExecute}
-						allowedCommands={allowedCommands}
-						setCachedStateField={setCachedStateField}
-					/>
-				</div>
-
-				<div ref={browserRef}>
-					<BrowserSettings
-						browserToolEnabled={browserToolEnabled}
-						browserViewportSize={browserViewportSize}
-						screenshotQuality={screenshotQuality}
-						remoteBrowserHost={remoteBrowserHost}
-						remoteBrowserEnabled={remoteBrowserEnabled}
-						setCachedStateField={setCachedStateField}
-					/>
-				</div>
-
-				<div ref={checkpointsRef}>
-					<CheckpointSettings
-						enableCheckpoints={enableCheckpoints}
-						setCachedStateField={setCachedStateField}
-					/>
-				</div>
-
-				<div ref={notificationsRef}>
-					<NotificationSettings
-						ttsEnabled={ttsEnabled}
-						ttsSpeed={ttsSpeed}
-						soundEnabled={soundEnabled}
-						soundVolume={soundVolume}
-						setCachedStateField={setCachedStateField}
-					/>
-				</div>
-
-				<div ref={contextManagementRef}>
-					<ContextManagementSettings
-						maxOpenTabsContext={maxOpenTabsContext}
-						maxWorkspaceFiles={maxWorkspaceFiles ?? 200}
-						showRooIgnoredFiles={showRooIgnoredFiles}
-						maxReadFileLine={maxReadFileLine}
-						setCachedStateField={setCachedStateField}
-					/>
-				</div>
-
-				<div ref={terminalRef}>
-					<TerminalSettings
-						terminalOutputLineLimit={terminalOutputLineLimit}
-						terminalShellIntegrationTimeout={terminalShellIntegrationTimeout}
-						terminalShellIntegrationDisabled={terminalShellIntegrationDisabled}
-						terminalCommandDelay={terminalCommandDelay}
-						terminalPowershellCounter={terminalPowershellCounter}
-						terminalZshClearEolMark={terminalZshClearEolMark}
-						terminalZshOhMy={terminalZshOhMy}
-						terminalZshP10k={terminalZshP10k}
-						terminalZdotdir={terminalZdotdir}
-						terminalCompressProgressBar={terminalCompressProgressBar}
-						setCachedStateField={setCachedStateField}
-					/>
-				</div>
-
-				<div ref={experimentalRef}>
-					<ExperimentalSettings setExperimentEnabled={setExperimentEnabled} experiments={experiments} />
-				</div>
-
-				<div ref={languageRef}>
-					<LanguageSettings language={language || "en"} setCachedStateField={setCachedStateField} />
-				</div>
+					)}
 
-				<div ref={aboutRef}>
-					<About
-						version={version}
-						telemetrySetting={telemetrySetting}
-						setTelemetrySetting={setTelemetrySetting}
-					/>
-				</div>
-			</TabContent>
+					{/* Checkpoints Section */}
+					{activeTab === "checkpoints" && (
+						<CheckpointSettings
+							enableCheckpoints={enableCheckpoints}
+							setCachedStateField={setCachedStateField}
+						/>
+					)}
+
+					{/* Notifications Section */}
+					{activeTab === "notifications" && (
+						<NotificationSettings
+							ttsEnabled={ttsEnabled}
+							ttsSpeed={ttsSpeed}
+							soundEnabled={soundEnabled}
+							soundVolume={soundVolume}
+							setCachedStateField={setCachedStateField}
+						/>
+					)}
+
+					{/* Context Management Section */}
+					{activeTab === "contextManagement" && (
+						<ContextManagementSettings
+							maxOpenTabsContext={maxOpenTabsContext}
+							maxWorkspaceFiles={maxWorkspaceFiles ?? 200}
+							showRooIgnoredFiles={showRooIgnoredFiles}
+							maxReadFileLine={maxReadFileLine}
+							setCachedStateField={setCachedStateField}
+						/>
+					)}
+
+					{/* Terminal Section */}
+					{activeTab === "terminal" && (
+						<TerminalSettings
+							terminalOutputLineLimit={terminalOutputLineLimit}
+							terminalShellIntegrationTimeout={terminalShellIntegrationTimeout}
+							terminalShellIntegrationDisabled={terminalShellIntegrationDisabled}
+							terminalCommandDelay={terminalCommandDelay}
+							terminalPowershellCounter={terminalPowershellCounter}
+							terminalZshClearEolMark={terminalZshClearEolMark}
+							terminalZshOhMy={terminalZshOhMy}
+							terminalZshP10k={terminalZshP10k}
+							terminalZdotdir={terminalZdotdir}
+							terminalCompressProgressBar={terminalCompressProgressBar}
+							setCachedStateField={setCachedStateField}
+						/>
+					)}
+
+					{/* Experimental Section */}
+					{activeTab === "experimental" && (
+						<ExperimentalSettings setExperimentEnabled={setExperimentEnabled} experiments={experiments} />
+					)}
+
+					{/* Language Section */}
+					{activeTab === "language" && (
+						<LanguageSettings language={language || "en"} setCachedStateField={setCachedStateField} />
+					)}
+
+					{/* About Section */}
+					{activeTab === "about" && (
+						<About
+							version={version}
+							telemetrySetting={telemetrySetting}
+							setTelemetrySetting={setTelemetrySetting}
+						/>
+					)}
+				</TabContent>
+			</div>
 
 			<AlertDialog open={isDiscardDialogShow} onOpenChange={setDiscardDialogShow}>
 				<AlertDialogContent>

+ 187 - 23
webview-ui/src/components/settings/__tests__/SettingsView.test.tsx

@@ -1,5 +1,4 @@
-// npx jest src/components/settings/__tests__/SettingsView.test.ts
-
+import React from "react"
 import { render, screen, fireEvent } from "@testing-library/react"
 import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
 
@@ -81,6 +80,47 @@ jest.mock("@vscode/webview-ui-toolkit/react", () => ({
 	VSCodeRadioGroup: ({ children, onChange }: any) => <div onChange={onChange}>{children}</div>,
 }))
 
+// Mock Tab components
+jest.mock("../../../components/common/Tab", () => ({
+	...jest.requireActual("../../../components/common/Tab"),
+	Tab: ({ children }: any) => <div data-testid="tab-container">{children}</div>,
+	TabHeader: ({ children }: any) => <div data-testid="tab-header">{children}</div>,
+	TabContent: ({ children }: any) => <div data-testid="tab-content">{children}</div>,
+	TabList: ({ children, value, onValueChange, "data-testid": dataTestId }: any) => {
+		// Store onValueChange in a global variable so TabTrigger can access it
+		;(window as any).__onValueChange = onValueChange
+		return (
+			<div data-testid={dataTestId} data-value={value}>
+				{children}
+			</div>
+		)
+	},
+	TabTrigger: ({ children, value, "data-testid": dataTestId, onClick, isSelected }: any) => {
+		// This function simulates clicking on a tab and making its content visible
+		const handleClick = () => {
+			if (onClick) onClick()
+			// Access onValueChange from the global variable
+			const onValueChange = (window as any).__onValueChange
+			if (onValueChange) onValueChange(value)
+			// Make all tab contents invisible
+			document.querySelectorAll("[data-tab-content]").forEach((el) => {
+				;(el as HTMLElement).style.display = "none"
+			})
+			// Make this tab's content visible
+			const tabContent = document.querySelector(`[data-tab-content="${value}"]`)
+			if (tabContent) {
+				;(tabContent as HTMLElement).style.display = "block"
+			}
+		}
+
+		return (
+			<button data-testid={dataTestId} data-value={value} data-selected={isSelected} onClick={handleClick}>
+				{children}
+			</button>
+		)
+	},
+}))
+
 // Mock Slider component
 jest.mock("@/components/ui", () => ({
 	...jest.requireActual("@/components/ui"),
@@ -129,7 +169,7 @@ const renderSettingsView = () => {
 	const onDone = jest.fn()
 	const queryClient = new QueryClient()
 
-	render(
+	const result = render(
 		<ExtensionStateContextProvider>
 			<QueryClientProvider client={queryClient}>
 				<SettingsView onDone={onDone} />
@@ -140,7 +180,20 @@ const renderSettingsView = () => {
 	// Hydrate initial state.
 	mockPostMessage({})
 
-	return { onDone }
+	// Helper function to activate a tab and ensure its content is visible
+	const activateTab = (tabId: string) => {
+		// Skip trying to find and click the tab, just directly render with the target section
+		// This bypasses the actual tab clicking mechanism but ensures the content is shown
+		result.rerender(
+			<ExtensionStateContextProvider>
+				<QueryClientProvider client={queryClient}>
+					<SettingsView onDone={onDone} targetSection={tabId} />
+				</QueryClientProvider>
+			</ExtensionStateContextProvider>,
+		)
+	}
+
+	return { onDone, activateTab }
 }
 
 describe("SettingsView - Sound Settings", () => {
@@ -149,7 +202,11 @@ describe("SettingsView - Sound Settings", () => {
 	})
 
 	it("initializes with tts disabled by default", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the notifications tab
+		activateTab("notifications")
 
 		const ttsCheckbox = screen.getByTestId("tts-enabled-checkbox")
 		expect(ttsCheckbox).not.toBeChecked()
@@ -159,7 +216,11 @@ describe("SettingsView - Sound Settings", () => {
 	})
 
 	it("initializes with sound disabled by default", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the notifications tab
+		activateTab("notifications")
 
 		const soundCheckbox = screen.getByTestId("sound-enabled-checkbox")
 		expect(soundCheckbox).not.toBeChecked()
@@ -169,7 +230,11 @@ describe("SettingsView - Sound Settings", () => {
 	})
 
 	it("toggles tts setting and sends message to VSCode", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the notifications tab
+		activateTab("notifications")
 
 		const ttsCheckbox = screen.getByTestId("tts-enabled-checkbox")
 
@@ -190,7 +255,11 @@ describe("SettingsView - Sound Settings", () => {
 	})
 
 	it("toggles sound setting and sends message to VSCode", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the notifications tab
+		activateTab("notifications")
 
 		const soundCheckbox = screen.getByTestId("sound-enabled-checkbox")
 
@@ -211,7 +280,11 @@ describe("SettingsView - Sound Settings", () => {
 	})
 
 	it("shows tts slider when sound is enabled", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the notifications tab
+		activateTab("notifications")
 
 		// Enable tts
 		const ttsCheckbox = screen.getByTestId("tts-enabled-checkbox")
@@ -224,7 +297,11 @@ describe("SettingsView - Sound Settings", () => {
 	})
 
 	it("shows volume slider when sound is enabled", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the notifications tab
+		activateTab("notifications")
 
 		// Enable sound
 		const soundCheckbox = screen.getByTestId("sound-enabled-checkbox")
@@ -237,7 +314,11 @@ describe("SettingsView - Sound Settings", () => {
 	})
 
 	it("updates speed and sends message to VSCode when slider changes", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the notifications tab
+		activateTab("notifications")
 
 		// Enable tts
 		const ttsCheckbox = screen.getByTestId("tts-enabled-checkbox")
@@ -259,7 +340,11 @@ describe("SettingsView - Sound Settings", () => {
 	})
 
 	it("updates volume and sends message to VSCode when slider changes", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the notifications tab
+		activateTab("notifications")
 
 		// Enable sound
 		const soundCheckbox = screen.getByTestId("sound-enabled-checkbox")
@@ -269,9 +354,9 @@ describe("SettingsView - Sound Settings", () => {
 		const volumeSlider = screen.getByTestId("sound-volume-slider")
 		fireEvent.change(volumeSlider, { target: { value: "0.75" } })
 
-		// Click Save to save settings
-		const saveButton = screen.getByTestId("save-button")
-		fireEvent.click(saveButton)
+		// Click Save to save settings - use getAllByTestId to handle multiple elements
+		const saveButtons = screen.getAllByTestId("save-button")
+		fireEvent.click(saveButtons[0])
 
 		// Verify message sent to VSCode
 		expect(vscode.postMessage).toHaveBeenCalledWith({
@@ -299,7 +384,11 @@ describe("SettingsView - Allowed Commands", () => {
 	})
 
 	it("shows allowed commands section when alwaysAllowExecute is enabled", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the autoApprove tab
+		activateTab("autoApprove")
 
 		// Enable always allow execute
 		const executeCheckbox = screen.getByTestId("always-allow-execute-toggle")
@@ -310,7 +399,11 @@ describe("SettingsView - Allowed Commands", () => {
 	})
 
 	it("adds new command to the list", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the autoApprove tab
+		activateTab("autoApprove")
 
 		// Enable always allow execute
 		const executeCheckbox = screen.getByTestId("always-allow-execute-toggle")
@@ -334,7 +427,11 @@ describe("SettingsView - Allowed Commands", () => {
 	})
 
 	it("removes command from the list", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the autoApprove tab
+		activateTab("autoApprove")
 
 		// Enable always allow execute
 		const executeCheckbox = screen.getByTestId("always-allow-execute-toggle")
@@ -360,8 +457,71 @@ describe("SettingsView - Allowed Commands", () => {
 		})
 	})
 
+	describe("SettingsView - Tab Navigation", () => {
+		beforeEach(() => {
+			jest.clearAllMocks()
+		})
+
+		it("renders with providers tab active by default", () => {
+			renderSettingsView()
+
+			// Check that the tab list is rendered
+			const tabList = screen.getByTestId("settings-tab-list")
+			expect(tabList).toBeInTheDocument()
+
+			// Check that providers content is visible
+			expect(screen.getByTestId("api-config-management")).toBeInTheDocument()
+		})
+
+		it("shows unsaved changes dialog when clicking Done with unsaved changes", () => {
+			// Render once and get the activateTab helper
+			const { activateTab } = renderSettingsView()
+
+			// Activate the notifications tab
+			activateTab("notifications")
+
+			// Make a change to create unsaved changes
+			const soundCheckbox = screen.getByTestId("sound-enabled-checkbox")
+			fireEvent.click(soundCheckbox)
+
+			// Click the Done button
+			const doneButton = screen.getByText("settings:common.done")
+			fireEvent.click(doneButton)
+
+			// Check that unsaved changes dialog is shown
+			expect(screen.getByText("settings:unsavedChangesDialog.title")).toBeInTheDocument()
+		})
+
+		it("renders with targetSection prop", () => {
+			// Render with a specific target section
+			render(
+				<ExtensionStateContextProvider>
+					<QueryClientProvider client={new QueryClient()}>
+						<SettingsView onDone={jest.fn()} targetSection="browser" />
+					</QueryClientProvider>
+				</ExtensionStateContextProvider>,
+			)
+
+			// Hydrate initial state
+			mockPostMessage({})
+
+			// Verify browser-related content is visible and API config is not
+			expect(screen.queryByTestId("api-config-management")).not.toBeInTheDocument()
+		})
+	})
+})
+
+describe("SettingsView - Duplicate Commands", () => {
+	beforeEach(() => {
+		jest.clearAllMocks()
+	})
+
 	it("prevents duplicate commands", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the autoApprove tab
+		activateTab("autoApprove")
 
 		// Enable always allow execute
 		const executeCheckbox = screen.getByTestId("always-allow-execute-toggle")
@@ -385,7 +545,11 @@ describe("SettingsView - Allowed Commands", () => {
 	})
 
 	it("saves allowed commands when clicking Save", () => {
-		renderSettingsView()
+		// Render once and get the activateTab helper
+		const { activateTab } = renderSettingsView()
+
+		// Activate the autoApprove tab
+		activateTab("autoApprove")
 
 		// Enable always allow execute
 		const executeCheckbox = screen.getByTestId("always-allow-execute-toggle")
@@ -397,9 +561,9 @@ describe("SettingsView - Allowed Commands", () => {
 		const addButton = screen.getByTestId("add-command-button")
 		fireEvent.click(addButton)
 
-		// Click Save
-		const saveButton = screen.getByTestId("save-button")
-		fireEvent.click(saveButton)
+		// Click Save - use getAllByTestId to handle multiple elements
+		const saveButtons = screen.getAllByTestId("save-button")
+		fireEvent.click(saveButtons[0])
 
 		// Verify VSCode messages were sent
 		expect(vscode.postMessage).toHaveBeenCalledWith(

+ 1 - 32
webview-ui/src/components/settings/styles.ts

@@ -1,37 +1,6 @@
 import styled from "styled-components"
 
-export const DropdownWrapper = styled.div`
-	position: relative;
-	width: 100%;
-`
-
-export const DropdownList = styled.div<{ $zIndex: number }>`
-	position: absolute;
-	top: calc(100% - 3px);
-	left: 0;
-	width: calc(100% - 2px);
-	max-height: 200px;
-	overflow-y: auto;
-	background-color: var(--vscode-dropdown-background);
-	border: 1px solid var(--vscode-list-activeSelectionBackground);
-	z-index: ${({ $zIndex }) => $zIndex};
-	border-bottom-left-radius: 3px;
-	border-bottom-right-radius: 3px;
-`
-
-export const DropdownItem = styled.div<{ $selected: boolean }>`
-	padding: 5px 10px;
-	cursor: pointer;
-	word-break: break-all;
-	white-space: normal;
-
-	background-color: ${({ $selected }) => ($selected ? "var(--vscode-list-activeSelectionBackground)" : "inherit")};
-
-	&:hover {
-		background-color: var(--vscode-list-activeSelectionBackground);
-	}
-`
-
+// Keep StyledMarkdown as it's used by ModelDescriptionMarkdown.tsx
 export const StyledMarkdown = styled.div`
 	font-family:
 		var(--vscode-font-family),

+ 5 - 7
webview-ui/src/i18n/locales/ca/settings.json

@@ -22,17 +22,15 @@
 	},
 	"sections": {
 		"providers": "Proveïdors",
-		"autoApprove": "Aprovació automàtica",
-		"browser": "Navegador / Ús de l'ordinador",
+		"autoApprove": "Auto-aprovació",
+		"browser": "Accés a l'ordinador",
 		"checkpoints": "Punts de control",
 		"notifications": "Notificacions",
-		"contextManagement": "Gestió de context",
+		"contextManagement": "Context",
 		"terminal": "Terminal",
-		"advanced": "Avançat",
-		"experimental": "Funcions experimentals",
+		"experimental": "Experimental",
 		"language": "Idioma",
-		"about": "Sobre Roo Code",
-		"interface": "Interfície"
+		"about": "Sobre Roo Code"
 	},
 	"autoApprove": {
 		"description": "Permet que Roo realitzi operacions automàticament sense requerir aprovació. Activeu aquesta configuració només si confieu plenament en la IA i enteneu els riscos de seguretat associats.",

+ 5 - 7
webview-ui/src/i18n/locales/de/settings.json

@@ -22,17 +22,15 @@
 	},
 	"sections": {
 		"providers": "Anbieter",
-		"autoApprove": "Automatische Genehmigung",
-		"browser": "Browser / Computer-Nutzung",
+		"autoApprove": "Auto-Genehmigung",
+		"browser": "Computerzugriff",
 		"checkpoints": "Kontrollpunkte",
 		"notifications": "Benachrichtigungen",
-		"contextManagement": "Kontext-Management",
+		"contextManagement": "Kontext",
 		"terminal": "Terminal",
-		"advanced": "Erweitert",
-		"experimental": "Experimentelle Funktionen",
+		"experimental": "Experimentell",
 		"language": "Sprache",
-		"about": "Über Roo Code",
-		"interface": "Oberfläche"
+		"about": "Über Roo Code"
 	},
 	"autoApprove": {
 		"description": "Erlaubt Roo, Operationen automatisch ohne Genehmigung durchzuführen. Aktiviere diese Einstellungen nur, wenn du der KI vollständig vertraust und die damit verbundenen Sicherheitsrisiken verstehst.",

+ 4 - 6
webview-ui/src/i18n/locales/en/settings.json

@@ -23,16 +23,14 @@
 	"sections": {
 		"providers": "Providers",
 		"autoApprove": "Auto-Approve",
-		"browser": "Browser / Computer Use",
+		"browser": "Browser",
 		"checkpoints": "Checkpoints",
 		"notifications": "Notifications",
-		"contextManagement": "Context Management",
+		"contextManagement": "Context",
 		"terminal": "Terminal",
-		"advanced": "Advanced",
-		"experimental": "Experimental Features",
+		"experimental": "Experimental",
 		"language": "Language",
-		"about": "About Roo Code",
-		"interface": "Interface"
+		"about": "About Roo Code"
 	},
 	"autoApprove": {
 		"description": "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.",

+ 5 - 7
webview-ui/src/i18n/locales/es/settings.json

@@ -22,17 +22,15 @@
 	},
 	"sections": {
 		"providers": "Proveedores",
-		"autoApprove": "Aprobación automática",
-		"browser": "Navegador / Uso del ordenador",
+		"autoApprove": "Auto-aprobación",
+		"browser": "Acceso al ordenador",
 		"checkpoints": "Puntos de control",
 		"notifications": "Notificaciones",
-		"contextManagement": "Gestión de contexto",
+		"contextManagement": "Contexto",
 		"terminal": "Terminal",
-		"advanced": "Avanzado",
-		"experimental": "Funciones experimentales",
+		"experimental": "Experimental",
 		"language": "Idioma",
-		"about": "Acerca de Roo Code",
-		"interface": "Interfaz"
+		"about": "Acerca de Roo Code"
 	},
 	"autoApprove": {
 		"description": "Permitir que Roo realice operaciones automáticamente sin requerir aprobación. Habilite esta configuración solo si confía plenamente en la IA y comprende los riesgos de seguridad asociados.",

+ 5 - 7
webview-ui/src/i18n/locales/fr/settings.json

@@ -22,17 +22,15 @@
 	},
 	"sections": {
 		"providers": "Fournisseurs",
-		"autoApprove": "Approbation automatique",
-		"browser": "Navigateur / Utilisation de l'ordinateur",
+		"autoApprove": "Auto-approbation",
+		"browser": "Accès ordinateur",
 		"checkpoints": "Points de contrôle",
 		"notifications": "Notifications",
-		"contextManagement": "Gestion du contexte",
+		"contextManagement": "Contexte",
 		"terminal": "Terminal",
-		"advanced": "Avancé",
-		"experimental": "Fonctionnalités expérimentales",
+		"experimental": "Expérimental",
 		"language": "Langue",
-		"about": "À propos de Roo Code",
-		"interface": "Interface"
+		"about": "À propos de Roo Code"
 	},
 	"autoApprove": {
 		"description": "Permettre à Roo d'effectuer automatiquement des opérations sans requérir d'approbation. Activez ces paramètres uniquement si vous faites entièrement confiance à l'IA et que vous comprenez les risques de sécurité associés.",

+ 5 - 7
webview-ui/src/i18n/locales/hi/settings.json

@@ -22,17 +22,15 @@
 	},
 	"sections": {
 		"providers": "प्रदाता",
-		"autoApprove": "स्वतः अनुमोदन",
-		"browser": "ब्राउज़र / कंप्यूटर उपयोग",
+		"autoApprove": "अनुमोदन",
+		"browser": "ब्राउज़र",
 		"checkpoints": "चेकपॉइंट",
 		"notifications": "सूचनाएँ",
-		"contextManagement": "संदर्भ प्रबंधन",
+		"contextManagement": "संदर्भ",
 		"terminal": "टर्मिनल",
-		"advanced": "उन्नत",
-		"experimental": "प्रायोगिक सुविधाएँ",
+		"experimental": "प्रायोगिक",
 		"language": "भाषा",
-		"about": "Roo Code के बारे में",
-		"interface": "इंटरफ़ेस"
+		"about": "परिचय"
 	},
 	"autoApprove": {
 		"description": "Roo को अनुमोदन की आवश्यकता के बिना स्वचालित रूप से ऑपरेशन करने की अनुमति दें। इन सेटिंग्स को केवल तभी सक्षम करें जब आप AI पर पूरी तरह से भरोसा करते हों और संबंधित सुरक्षा जोखिमों को समझते हों।",

+ 5 - 7
webview-ui/src/i18n/locales/it/settings.json

@@ -22,17 +22,15 @@
 	},
 	"sections": {
 		"providers": "Fornitori",
-		"autoApprove": "Approvazione automatica",
-		"browser": "Browser / Uso del computer",
+		"autoApprove": "Auto-approvazione",
+		"browser": "Accesso computer",
 		"checkpoints": "Punti di controllo",
 		"notifications": "Notifiche",
-		"contextManagement": "Gestione del contesto",
+		"contextManagement": "Contesto",
 		"terminal": "Terminal",
-		"advanced": "Avanzate",
-		"experimental": "Funzionalità sperimentali",
+		"experimental": "Sperimentale",
 		"language": "Lingua",
-		"about": "Informazioni su Roo Code",
-		"interface": "Interfaccia"
+		"about": "Informazioni su Roo Code"
 	},
 	"autoApprove": {
 		"description": "Permetti a Roo di eseguire automaticamente operazioni senza richiedere approvazione. Abilita queste impostazioni solo se ti fidi completamente dell'IA e comprendi i rischi di sicurezza associati.",

+ 4 - 6
webview-ui/src/i18n/locales/ja/settings.json

@@ -23,16 +23,14 @@
 	"sections": {
 		"providers": "プロバイダー",
 		"autoApprove": "自動承認",
-		"browser": "ブラウザ / コンピューター使用",
+		"browser": "コンピューターアクセス",
 		"checkpoints": "チェックポイント",
 		"notifications": "通知",
-		"contextManagement": "コンテキスト管理",
+		"contextManagement": "コンテキスト",
 		"terminal": "ターミナル",
-		"advanced": "詳細設定",
-		"experimental": "実験的機能",
+		"experimental": "実験的",
 		"language": "言語",
-		"about": "Roo Codeについて",
-		"interface": "インターフェース"
+		"about": "Roo Codeについて"
 	},
 	"autoApprove": {
 		"description": "Rooが承認なしで自動的に操作を実行できるようにします。AIを完全に信頼し、関連するセキュリティリスクを理解している場合にのみ、これらの設定を有効にしてください。",

+ 4 - 6
webview-ui/src/i18n/locales/ko/settings.json

@@ -23,16 +23,14 @@
 	"sections": {
 		"providers": "공급자",
 		"autoApprove": "자동 승인",
-		"browser": "브라우저 / 컴퓨터 사용",
+		"browser": "컴퓨터 접근",
 		"checkpoints": "체크포인트",
 		"notifications": "알림",
-		"contextManagement": "컨텍스트 관리",
+		"contextManagement": "컨텍스트",
 		"terminal": "터미널",
-		"advanced": "고급",
-		"experimental": "실험적 기능",
+		"experimental": "실험적",
 		"language": "언어",
-		"about": "Roo Code 정보",
-		"interface": "인터페이스"
+		"about": "Roo Code 정보"
 	},
 	"autoApprove": {
 		"description": "Roo가 승인 없이 자동으로 작업을 수행할 수 있도록 허용합니다. AI를 완전히 신뢰하고 관련 보안 위험을 이해하는 경우에만 이러한 설정을 활성화하세요.",

+ 5 - 7
webview-ui/src/i18n/locales/pl/settings.json

@@ -22,17 +22,15 @@
 	},
 	"sections": {
 		"providers": "Dostawcy",
-		"autoApprove": "Automatyczne zatwierdzanie",
-		"browser": "Przeglądarka / Użycie komputera",
+		"autoApprove": "Auto-zatwierdzanie",
+		"browser": "Dostęp komputera",
 		"checkpoints": "Punkty kontrolne",
 		"notifications": "Powiadomienia",
-		"contextManagement": "Zarządzanie kontekstem",
+		"contextManagement": "Kontekst",
 		"terminal": "Terminal",
-		"advanced": "Zaawansowane",
-		"experimental": "Funkcje eksperymentalne",
+		"experimental": "Eksperymentalne",
 		"language": "Język",
-		"about": "O Roo Code",
-		"interface": "Interfejs"
+		"about": "O Roo Code"
 	},
 	"autoApprove": {
 		"description": "Pozwól Roo na automatyczne wykonywanie operacji bez wymagania zatwierdzenia. Włącz te ustawienia tylko jeśli w pełni ufasz AI i rozumiesz związane z tym zagrożenia bezpieczeństwa.",

+ 6 - 8
webview-ui/src/i18n/locales/pt-BR/settings.json

@@ -22,17 +22,15 @@
 	},
 	"sections": {
 		"providers": "Provedores",
-		"autoApprove": "Aprovação automática",
-		"browser": "Navegador / Uso do computador",
-		"checkpoints": "Pontos de verificação",
+		"autoApprove": "Aprovação",
+		"browser": "Navegador",
+		"checkpoints": "Checkpoints",
 		"notifications": "Notificações",
-		"contextManagement": "Gestão de contexto",
+		"contextManagement": "Contexto",
 		"terminal": "Terminal",
-		"advanced": "Avançado",
-		"experimental": "Recursos experimentais",
+		"experimental": "Experimental",
 		"language": "Idioma",
-		"about": "Sobre o Roo Code",
-		"interface": "Interface"
+		"about": "Sobre"
 	},
 	"autoApprove": {
 		"description": "Permitir que o Roo realize operações automaticamente sem exigir aprovação. Ative essas configurações apenas se confiar totalmente na IA e compreender os riscos de segurança associados.",

+ 4 - 6
webview-ui/src/i18n/locales/ru/settings.json

@@ -23,16 +23,14 @@
 	"sections": {
 		"providers": "Провайдеры",
 		"autoApprove": "Автоодобрение",
-		"browser": "Браузер / Использование компьютера",
+		"browser": "Доступ к компьютеру",
 		"checkpoints": "Контрольные точки",
 		"notifications": "Уведомления",
-		"contextManagement": "Управление контекстом",
+		"contextManagement": "Контекст",
 		"terminal": "Терминал",
-		"advanced": "Дополнительно",
-		"experimental": "Экспериментальные функции",
+		"experimental": "Экспериментальное",
 		"language": "Язык",
-		"about": "О Roo Code",
-		"interface": "Интерфейс"
+		"about": "О Roo Code"
 	},
 	"autoApprove": {
 		"description": "Разрешить Roo автоматически выполнять операции без необходимости одобрения. Включайте эти параметры только если полностью доверяете ИИ и понимаете связанные с этим риски безопасности.",

+ 5 - 7
webview-ui/src/i18n/locales/tr/settings.json

@@ -22,17 +22,15 @@
 	},
 	"sections": {
 		"providers": "Sağlayıcılar",
-		"autoApprove": "Otomatik Onay",
-		"browser": "Tarayıcı / Bilgisayar Kullanımı",
+		"autoApprove": "Oto-Onay",
+		"browser": "Bilgisayar Erişimi",
 		"checkpoints": "Kontrol Noktaları",
 		"notifications": "Bildirimler",
-		"contextManagement": "Bağlam Yönetimi",
+		"contextManagement": "Bağlam",
 		"terminal": "Terminal",
-		"advanced": "Gelişmiş",
-		"experimental": "Deneysel Özellikler",
+		"experimental": "Deneysel",
 		"language": "Dil",
-		"about": "Roo Code Hakkında",
-		"interface": "Arayüz"
+		"about": "Roo Code Hakkında"
 	},
 	"autoApprove": {
 		"description": "Roo'nun onay gerektirmeden otomatik olarak işlemler gerçekleştirmesine izin verin. Bu ayarları yalnızca yapay zekaya tamamen güveniyorsanız ve ilgili güvenlik risklerini anlıyorsanız etkinleştirin.",

+ 5 - 7
webview-ui/src/i18n/locales/vi/settings.json

@@ -22,17 +22,15 @@
 	},
 	"sections": {
 		"providers": "Nhà cung cấp",
-		"autoApprove": "Tự động phê duyệt",
-		"browser": "Trình duyệt / Sử dụng máy tính",
+		"autoApprove": "Phê duyệt",
+		"browser": "Trình duyệt",
 		"checkpoints": "Điểm kiểm tra",
 		"notifications": "Thông báo",
-		"contextManagement": "Quản lý ngữ cảnh",
+		"contextManagement": "Ngữ cảnh",
 		"terminal": "Terminal",
-		"advanced": "Nâng cao",
-		"experimental": "Tính năng thử nghiệm",
+		"experimental": "Thử nghiệm",
 		"language": "Ngôn ngữ",
-		"about": "Về Roo Code",
-		"interface": "Giao diện"
+		"about": "Giới thiệu"
 	},
 	"autoApprove": {
 		"description": "Cho phép Roo tự động thực hiện các hoạt động mà không cần phê duyệt. Chỉ bật những cài đặt này nếu bạn hoàn toàn tin tưởng AI và hiểu rõ các rủi ro bảo mật liên quan.",

+ 3 - 5
webview-ui/src/i18n/locales/zh-CN/settings.json

@@ -23,14 +23,12 @@
 	"sections": {
 		"providers": "提供商",
 		"autoApprove": "自动批准",
-		"browser": "浏览器交互",
+		"browser": "计算机交互",
 		"checkpoints": "检查点",
-		"interface": "界面内容",
 		"notifications": "通知",
-		"contextManagement": "上下文管理",
+		"contextManagement": "上下文",
 		"terminal": "终端",
-		"advanced": "高级",
-		"experimental": "实验性功能",
+		"experimental": "实验性",
 		"language": "语言",
 		"about": "关于 Roo Code"
 	},

+ 4 - 6
webview-ui/src/i18n/locales/zh-TW/settings.json

@@ -23,16 +23,14 @@
 	"sections": {
 		"providers": "供應商",
 		"autoApprove": "自動核准",
-		"browser": "瀏覽器/電腦使用",
+		"browser": "電腦存取",
 		"checkpoints": "檢查點",
 		"notifications": "通知",
-		"contextManagement": "上下文管理",
+		"contextManagement": "上下文",
 		"terminal": "終端機",
-		"advanced": "進階",
-		"experimental": "實驗性功能",
+		"experimental": "實驗性",
 		"language": "語言",
-		"about": "關於 Roo Code",
-		"interface": "介面"
+		"about": "關於 Roo Code"
 	},
 	"autoApprove": {
 		"description": "允許 Roo 無需核准即執行操作。僅在您完全信任 AI 並了解相關安全風險時啟用這些設定。",