Explorar o código

Use the Slider component everywhere (#1786)

* Use the Slider component everywhere

* onValueCommit -> onValueChange

* More cleanup

* Fix tests

* Add missing translations

* Update webview-ui/src/components/settings/ContextManagementSettings.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* SelectDropdown fixes

* Fix tests

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Chris Estreich hai 9 meses
pai
achega
f4abdf4b95
Modificáronse 40 ficheiros con 689 adicións e 751 borrados
  1. 19 5
      scripts/find-missing-translations.js
  2. 0 12
      webview-ui/src/__mocks__/@vscode/webview-ui-toolkit/react.ts
  3. 2 2
      webview-ui/src/__mocks__/vscrui.ts
  4. 70 102
      webview-ui/src/components/settings/AdvancedSettings.tsx
  5. 72 102
      webview-ui/src/components/settings/ApiConfigManager.tsx
  6. 7 7
      webview-ui/src/components/settings/ApiOptions.tsx
  7. 116 123
      webview-ui/src/components/settings/AutoApproveSettings.tsx
  8. 100 110
      webview-ui/src/components/settings/BrowserSettings.tsx
  9. 35 41
      webview-ui/src/components/settings/ContextManagementSettings.tsx
  10. 1 1
      webview-ui/src/components/settings/LanguageSettings.tsx
  11. 4 1
      webview-ui/src/components/settings/ModelPicker.tsx
  12. 42 42
      webview-ui/src/components/settings/NotificationSettings.tsx
  13. 1 1
      webview-ui/src/components/settings/Section.tsx
  14. 0 1
      webview-ui/src/components/settings/SettingsView.tsx
  15. 28 26
      webview-ui/src/components/settings/TemperatureControl.tsx
  16. 34 44
      webview-ui/src/components/settings/TerminalSettings.tsx
  17. 26 26
      webview-ui/src/components/settings/__tests__/ApiConfigManager.test.tsx
  18. 23 3
      webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx
  19. 16 14
      webview-ui/src/components/settings/__tests__/SettingsView.test.tsx
  20. 32 42
      webview-ui/src/components/settings/__tests__/TemperatureControl.test.tsx
  21. 0 1
      webview-ui/src/components/settings/__tests__/ThinkingBudget.test.tsx
  22. 0 7
      webview-ui/src/components/settings/styles.ts
  23. 7 1
      webview-ui/src/components/ui/__tests__/select-dropdown.test.tsx
  24. 8 6
      webview-ui/src/components/ui/select-dropdown.tsx
  25. 1 1
      webview-ui/src/components/ui/slider.tsx
  26. 3 2
      webview-ui/src/i18n/locales/ca/settings.json
  27. 3 2
      webview-ui/src/i18n/locales/de/settings.json
  28. 3 2
      webview-ui/src/i18n/locales/en/settings.json
  29. 3 2
      webview-ui/src/i18n/locales/es/settings.json
  30. 3 2
      webview-ui/src/i18n/locales/fr/settings.json
  31. 3 2
      webview-ui/src/i18n/locales/hi/settings.json
  32. 3 2
      webview-ui/src/i18n/locales/it/settings.json
  33. 3 2
      webview-ui/src/i18n/locales/ja/settings.json
  34. 3 2
      webview-ui/src/i18n/locales/ko/settings.json
  35. 3 2
      webview-ui/src/i18n/locales/pl/settings.json
  36. 3 2
      webview-ui/src/i18n/locales/pt-BR/settings.json
  37. 3 2
      webview-ui/src/i18n/locales/tr/settings.json
  38. 3 2
      webview-ui/src/i18n/locales/vi/settings.json
  39. 3 2
      webview-ui/src/i18n/locales/zh-CN/settings.json
  40. 3 2
      webview-ui/src/i18n/locales/zh-TW/settings.json

+ 19 - 5
scripts/find-missing-translations.js

@@ -141,10 +141,17 @@ function checkAreaTranslations(area) {
 	}
 
 	// Load file contents
-	const englishFileContents = englishFiles.map((file) => ({
-		name: file,
-		content: JSON.parse(fs.readFileSync(path.join(englishDir, file), "utf8")),
-	}))
+	let englishFileContents
+
+	try {
+		englishFileContents = englishFiles.map((file) => ({
+			name: file,
+			content: JSON.parse(fs.readFileSync(path.join(englishDir, file), "utf8")),
+		}))
+	} catch (e) {
+		console.error(`Error: File '${englishDir}' is not a valid JSON file`)
+		process.exit(1)
+	}
 
 	console.log(
 		`Checking ${englishFileContents.length} translation file(s): ${englishFileContents.map((f) => f.name).join(", ")}`,
@@ -167,7 +174,14 @@ function checkAreaTranslations(area) {
 			}
 
 			// Load the locale file
-			const localeContent = JSON.parse(fs.readFileSync(localeFilePath, "utf8"))
+			let localeContent
+
+			try {
+				localeContent = JSON.parse(fs.readFileSync(localeFilePath, "utf8"))
+			} catch (e) {
+				console.error(`Error: File '${localeFilePath}' is not a valid JSON file`)
+				process.exit(1)
+			}
 
 			// Find all keys in the English file
 			const englishKeys = findKeys(englishContent)

+ 0 - 12
webview-ui/src/__mocks__/@vscode/webview-ui-toolkit/react.ts

@@ -103,15 +103,3 @@ export const VSCodeRadio: React.FC<VSCodeProps> = ({ children, value, checked, o
 
 export const VSCodeRadioGroup: React.FC<VSCodeProps> = ({ children, onChange, ...props }) =>
 	React.createElement("div", { role: "radiogroup", onChange, ...props }, children)
-
-export const VSCodeSlider: React.FC<VSCodeProps> = ({ value, onChange, ...props }) =>
-	React.createElement("input", {
-		type: "range",
-		value,
-		onChange: (e: any) => onChange?.({ target: { value: Number(e.target.value) } }),
-		min: 0,
-		max: 1,
-		step: 0.01,
-		style: { flexGrow: 1, height: "2px" },
-		...props,
-	})

+ 2 - 2
webview-ui/src/__mocks__/vscrui.ts

@@ -1,9 +1,9 @@
 import React from "react"
 
-export const Checkbox = ({ children, checked, onChange }: any) =>
+export const Checkbox = ({ children, onChange }: any) =>
 	React.createElement("div", { "data-testid": "mock-checkbox", onClick: onChange }, children)
 
-export const Dropdown = ({ children, value, onChange }: any) =>
+export const Dropdown = ({ children, onChange }: any) =>
 	React.createElement("div", { "data-testid": "mock-dropdown", onClick: onChange }, children)
 
 export const Pane = ({ children }: any) => React.createElement("div", { "data-testid": "mock-pane" }, children)

+ 70 - 102
webview-ui/src/components/settings/AdvancedSettings.tsx

@@ -6,26 +6,22 @@ import { Cog } from "lucide-react"
 import { EXPERIMENT_IDS, ExperimentId } from "../../../../src/shared/experiments"
 
 import { cn } from "@/lib/utils"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider } from "@/components/ui"
 
 import { SetCachedStateField, SetExperimentEnabled } from "./types"
-import { sliderLabelStyle } from "./styles"
 import { SectionHeader } from "./SectionHeader"
 import { Section } from "./Section"
 
 type AdvancedSettingsProps = HTMLAttributes<HTMLDivElement> & {
 	rateLimitSeconds: number
-	terminalShellIntegrationTimeout: number | undefined
 	diffEnabled?: boolean
 	fuzzyMatchThreshold?: number
-	setCachedStateField: SetCachedStateField<
-		"rateLimitSeconds" | "diffEnabled" | "fuzzyMatchThreshold" | "terminalShellIntegrationTimeout"
-	>
+	setCachedStateField: SetCachedStateField<"rateLimitSeconds" | "diffEnabled" | "fuzzyMatchThreshold">
 	experiments: Record<ExperimentId, boolean>
 	setExperimentEnabled: SetExperimentEnabled
 }
 export const AdvancedSettings = ({
 	rateLimitSeconds,
-	terminalShellIntegrationTimeout,
 	diffEnabled,
 	fuzzyMatchThreshold,
 	setCachedStateField,
@@ -35,6 +31,7 @@ export const AdvancedSettings = ({
 	...props
 }: AdvancedSettingsProps) => {
 	const { t } = useAppTranslation()
+
 	return (
 		<div className={cn("flex flex-col gap-2", className)} {...props}>
 			<SectionHeader>
@@ -49,50 +46,18 @@ export const AdvancedSettings = ({
 					<div className="flex flex-col gap-2">
 						<span className="font-medium">{t("settings:advanced.rateLimit.label")}</span>
 						<div className="flex items-center gap-2">
-							<input
-								type="range"
-								min="0"
-								max="60"
-								step="1"
-								value={rateLimitSeconds}
-								onChange={(e) => setCachedStateField("rateLimitSeconds", parseInt(e.target.value))}
-								className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
+							<Slider
+								min={0}
+								max={60}
+								step={1}
+								value={[rateLimitSeconds]}
+								onValueChange={([value]) => setCachedStateField("rateLimitSeconds", value)}
 							/>
-							<span style={{ ...sliderLabelStyle }}>{rateLimitSeconds}s</span>
+							<span className="w-10">{rateLimitSeconds}s</span>
 						</div>
 					</div>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:advanced.rateLimit.description")}
-					</p>
-				</div>
-
-				<div>
-					<div className="flex flex-col gap-2">
-						<span className="font-medium">Terminal shell integration timeout</span>
-						<div className="flex items-center gap-2">
-							<input
-								type="range"
-								min="1000"
-								max="60000"
-								step="1000"
-								value={terminalShellIntegrationTimeout}
-								onChange={(e) =>
-									setCachedStateField(
-										"terminalShellIntegrationTimeout",
-										Math.min(60000, Math.max(1000, parseInt(e.target.value))),
-									)
-								}
-								className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
-							/>
-							<span style={{ ...sliderLabelStyle }}>
-								{(terminalShellIntegrationTimeout ?? 4000) / 1000}s
-							</span>
-						</div>
-						<p className="text-vscode-descriptionForeground text-sm mt-0">
-							Maximum time to wait for shell integration to initialize before executing commands. For
-							users with long shell startup times, this value may need to be increased if you see "Shell
-							Integration Unavailable" errors in the terminal.
-						</p>
 					</div>
 				</div>
 
@@ -109,48 +74,53 @@ export const AdvancedSettings = ({
 						}}>
 						<span className="font-medium">{t("settings:advanced.diff.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm">
 						{t("settings:advanced.diff.description")}
-					</p>
-					{diffEnabled && (
-						<div className="flex flex-col gap-2 mt-3 mb-2 pl-3 border-l-2 border-vscode-button-background">
-							<div className="flex flex-col gap-2">
-								<span className="font-medium">{t("settings:advanced.diff.strategy.label")}</span>
-								<select
-									value={
-										experiments[EXPERIMENT_IDS.DIFF_STRATEGY]
-											? "unified"
-											: experiments[EXPERIMENT_IDS.MULTI_SEARCH_AND_REPLACE]
-												? "multiBlock"
-												: "standard"
+					</div>
+				</div>
+
+				{diffEnabled && (
+					<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
+						<div>
+							<label className="block font-medium mb-1">
+								{t("settings:advanced.diff.strategy.label")}
+							</label>
+							<Select
+								value={
+									experiments[EXPERIMENT_IDS.DIFF_STRATEGY]
+										? "unified"
+										: experiments[EXPERIMENT_IDS.MULTI_SEARCH_AND_REPLACE]
+											? "multiBlock"
+											: "standard"
+								}
+								onValueChange={(value) => {
+									if (value === "standard") {
+										setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY, false)
+										setExperimentEnabled(EXPERIMENT_IDS.MULTI_SEARCH_AND_REPLACE, false)
+									} else if (value === "unified") {
+										setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY, true)
+										setExperimentEnabled(EXPERIMENT_IDS.MULTI_SEARCH_AND_REPLACE, false)
+									} else if (value === "multiBlock") {
+										setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY, false)
+										setExperimentEnabled(EXPERIMENT_IDS.MULTI_SEARCH_AND_REPLACE, true)
 									}
-									onChange={(e) => {
-										const value = e.target.value
-										if (value === "standard") {
-											setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY, false)
-											setExperimentEnabled(EXPERIMENT_IDS.MULTI_SEARCH_AND_REPLACE, false)
-										} else if (value === "unified") {
-											setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY, true)
-											setExperimentEnabled(EXPERIMENT_IDS.MULTI_SEARCH_AND_REPLACE, false)
-										} else if (value === "multiBlock") {
-											setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY, false)
-											setExperimentEnabled(EXPERIMENT_IDS.MULTI_SEARCH_AND_REPLACE, true)
-										}
-									}}
-									className="p-2 rounded w-full bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border outline-none focus:border-vscode-focusBorder">
-									<option value="standard">
+								}}>
+								<SelectTrigger className="w-full">
+									<SelectValue />
+								</SelectTrigger>
+								<SelectContent>
+									<SelectItem value="standard">
 										{t("settings:advanced.diff.strategy.options.standard")}
-									</option>
-									<option value="multiBlock">
+									</SelectItem>
+									<SelectItem value="multiBlock">
 										{t("settings:advanced.diff.strategy.options.multiBlock")}
-									</option>
-									<option value="unified">
+									</SelectItem>
+									<SelectItem value="unified">
 										{t("settings:advanced.diff.strategy.options.unified")}
-									</option>
-								</select>
-							</div>
-
-							<p className="text-vscode-descriptionForeground text-sm mt-1">
+									</SelectItem>
+								</SelectContent>
+							</Select>
+							<div className="text-vscode-descriptionForeground text-sm mt-1">
 								{!experiments[EXPERIMENT_IDS.DIFF_STRATEGY] &&
 									!experiments[EXPERIMENT_IDS.MULTI_SEARCH_AND_REPLACE] &&
 									t("settings:advanced.diff.strategy.descriptions.standard")}
@@ -158,31 +128,29 @@ export const AdvancedSettings = ({
 									t("settings:advanced.diff.strategy.descriptions.unified")}
 								{experiments[EXPERIMENT_IDS.MULTI_SEARCH_AND_REPLACE] &&
 									t("settings:advanced.diff.strategy.descriptions.multiBlock")}
-							</p>
+							</div>
+						</div>
 
-							<span className="font-medium mt-3">{t("settings:advanced.diff.matchPrecision.label")}</span>
+						<div>
+							<label className="block font-medium mb-1">
+								{t("settings:advanced.diff.matchPrecision.label")}
+							</label>
 							<div className="flex items-center gap-2">
-								<input
-									type="range"
-									min="0.8"
-									max="1"
-									step="0.005"
-									value={fuzzyMatchThreshold ?? 1.0}
-									onChange={(e) => {
-										setCachedStateField("fuzzyMatchThreshold", parseFloat(e.target.value))
-									}}
-									className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
+								<Slider
+									min={0.8}
+									max={1}
+									step={0.005}
+									value={[fuzzyMatchThreshold ?? 1.0]}
+									onValueChange={([value]) => setCachedStateField("fuzzyMatchThreshold", value)}
 								/>
-								<span style={{ ...sliderLabelStyle }}>
-									{Math.round((fuzzyMatchThreshold || 1) * 100)}%
-								</span>
+								<span className="w-10">{Math.round((fuzzyMatchThreshold || 1) * 100)}%</span>
 							</div>
-							<p className="text-vscode-descriptionForeground text-sm mt-0">
+							<div className="text-vscode-descriptionForeground text-sm mt-1">
 								{t("settings:advanced.diff.matchPrecision.description")}
-							</p>
+							</div>
 						</div>
-					)}
-				</div>
+					</div>
+				)}
 			</Section>
 		</div>
 	)

+ 72 - 102
webview-ui/src/components/settings/ApiConfigManager.tsx

@@ -1,11 +1,21 @@
-import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { memo, useEffect, useRef, useState } from "react"
-import { useAppTranslation } from "@/i18n/TranslationContext"
+import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
+
 import { ApiConfigMeta } from "../../../../src/shared/ExtensionMessage"
-import { Dropdown } from "vscrui"
-import type { DropdownOption } from "vscrui"
-import { Dialog, DialogContent, DialogTitle } from "../ui/dialog"
-import { Button, Input } from "../ui"
+
+import { useAppTranslation } from "@/i18n/TranslationContext"
+import {
+	Button,
+	Input,
+	Dialog,
+	DialogContent,
+	DialogTitle,
+	Select,
+	SelectTrigger,
+	SelectValue,
+	SelectContent,
+	SelectItem,
+} from "@/components/ui"
 
 interface ApiConfigManagerProps {
 	currentApiConfigName?: string
@@ -25,6 +35,7 @@ const ApiConfigManager = ({
 	onUpsertConfig,
 }: ApiConfigManagerProps) => {
 	const { t } = useAppTranslation()
+
 	const [isRenaming, setIsRenaming] = useState(false)
 	const [isCreating, setIsCreating] = useState(false)
 	const [inputValue, setInputValue] = useState("")
@@ -39,12 +50,12 @@ const ApiConfigManager = ({
 
 		const nameExists = listApiConfigMeta?.some((config) => config.name.toLowerCase() === trimmed.toLowerCase())
 
-		// For new profiles, any existing name is invalid
+		// For new profiles, any existing name is invalid.
 		if (isNewProfile && nameExists) {
 			return t("settings:providers.nameExists")
 		}
 
-		// For rename, only block if trying to rename to a different existing profile
+		// For rename, only block if trying to rename to a different existing profile.
 		if (!isNewProfile && nameExists && trimmed.toLowerCase() !== currentApiConfigName?.toLowerCase()) {
 			return t("settings:providers.nameExists")
 		}
@@ -64,7 +75,7 @@ const ApiConfigManager = ({
 		setError(null)
 	}
 
-	// Focus input when entering rename mode
+	// Focus input when entering rename mode.
 	useEffect(() => {
 		if (isRenaming) {
 			const timeoutId = setTimeout(() => inputRef.current?.focus(), 0)
@@ -72,7 +83,7 @@ const ApiConfigManager = ({
 		}
 	}, [isRenaming])
 
-	// Focus input when opening new dialog
+	// Focus input when opening new dialog.
 	useEffect(() => {
 		if (isCreating) {
 			const timeoutId = setTimeout(() => newProfileInputRef.current?.focus(), 0)
@@ -80,7 +91,7 @@ const ApiConfigManager = ({
 		}
 	}, [isCreating])
 
-	// Reset state when current profile changes
+	// Reset state when current profile changes.
 	useEffect(() => {
 		resetCreateState()
 		resetRenameState()
@@ -137,7 +148,7 @@ const ApiConfigManager = ({
 	const handleDelete = () => {
 		if (!currentApiConfigName || !listApiConfigMeta || listApiConfigMeta.length <= 1) return
 
-		// Let the extension handle both deletion and selection
+		// Let the extension handle both deletion and selection.
 		onDeleteConfig(currentApiConfigName)
 	}
 
@@ -145,15 +156,11 @@ const ApiConfigManager = ({
 
 	return (
 		<div className="flex flex-col gap-1">
-			<label htmlFor="config-profile">
-				<span className="font-medium">{t("settings:providers.configProfile")}</span>
-			</label>
+			<label className="block font-medium mb-1">{t("settings:providers.configProfile")}</label>
 
 			{isRenaming ? (
-				<div
-					data-testid="rename-form"
-					style={{ display: "flex", gap: "4px", alignItems: "center", flexDirection: "column" }}>
-					<div style={{ display: "flex", gap: "4px", alignItems: "center", width: "100%" }}>
+				<div data-testid="rename-form">
+					<div className="flex items-center gap-1">
 						<VSCodeTextField
 							ref={inputRef}
 							value={inputValue}
@@ -163,100 +170,75 @@ const ApiConfigManager = ({
 								setError(null)
 							}}
 							placeholder={t("settings:providers.enterNewName")}
-							style={{ flexGrow: 1 }}
-							onKeyDown={(e: unknown) => {
-								const event = e as { key: string }
-								if (event.key === "Enter" && inputValue.trim()) {
+							onKeyDown={({ key }) => {
+								if (key === "Enter" && inputValue.trim()) {
 									handleSave()
-								} else if (event.key === "Escape") {
+								} else if (key === "Escape") {
 									handleCancel()
 								}
 							}}
+							className="grow"
 						/>
-						<VSCodeButton
-							appearance="icon"
+						<Button
+							variant="ghost"
+							size="icon"
 							disabled={!inputValue.trim()}
 							onClick={handleSave}
 							title={t("settings:common.save")}
-							data-testid="save-rename-button"
-							style={{
-								padding: 0,
-								margin: 0,
-								height: "28px",
-								width: "28px",
-								minWidth: "28px",
-							}}>
+							data-testid="save-rename-button">
 							<span className="codicon codicon-check" />
-						</VSCodeButton>
-						<VSCodeButton
-							appearance="icon"
+						</Button>
+						<Button
+							variant="ghost"
+							size="icon"
 							onClick={handleCancel}
 							title={t("settings:common.cancel")}
-							data-testid="cancel-rename-button"
-							style={{
-								padding: 0,
-								margin: 0,
-								height: "28px",
-								width: "28px",
-								minWidth: "28px",
-							}}>
+							data-testid="cancel-rename-button">
 							<span className="codicon codicon-close" />
-						</VSCodeButton>
+						</Button>
 					</div>
 					{error && (
-						<p className="text-red-500 text-sm mt-2" data-testid="error-message">
+						<div className="text-vscode-descriptionForeground text-sm mt-1" data-testid="error-message">
 							{error}
-						</p>
+						</div>
 					)}
 				</div>
 			) : (
 				<>
-					<div style={{ display: "flex", gap: "4px", alignItems: "center" }}>
-						<Dropdown
-							id="config-profile"
-							value={currentApiConfigName}
-							onChange={(value: unknown) => {
-								onSelectConfig((value as DropdownOption).value)
-							}}
-							role="combobox"
-							options={listApiConfigMeta.map((config) => ({
-								value: config.name,
-								label: config.name,
-							}))}
-							className="w-full"
-						/>
-						<VSCodeButton
-							appearance="icon"
+					<div className="flex items-center gap-1">
+						<Select value={currentApiConfigName} onValueChange={onSelectConfig}>
+							<SelectTrigger className="grow">
+								<SelectValue placeholder={t("settings:common.select")} />
+							</SelectTrigger>
+							<SelectContent>
+								{listApiConfigMeta.map((config) => (
+									<SelectItem key={config.name} value={config.name}>
+										{config.name}
+									</SelectItem>
+								))}
+							</SelectContent>
+						</Select>
+						<Button
+							variant="ghost"
+							size="icon"
 							onClick={handleAdd}
 							title={t("settings:providers.addProfile")}
-							data-testid="add-profile-button"
-							style={{
-								padding: 0,
-								margin: 0,
-								height: "28px",
-								width: "28px",
-								minWidth: "28px",
-							}}>
+							data-testid="add-profile-button">
 							<span className="codicon codicon-add" />
-						</VSCodeButton>
+						</Button>
 						{currentApiConfigName && (
 							<>
-								<VSCodeButton
-									appearance="icon"
+								<Button
+									variant="ghost"
+									size="icon"
 									onClick={handleStartRename}
 									title={t("settings:providers.renameProfile")}
-									data-testid="rename-profile-button"
-									style={{
-										padding: 0,
-										margin: 0,
-										height: "28px",
-										width: "28px",
-										minWidth: "28px",
-									}}>
+									data-testid="rename-profile-button">
 									<span className="codicon codicon-edit" />
-								</VSCodeButton>
-								<VSCodeButton
-									appearance="icon"
+								</Button>
+								<Button
+									variant="ghost"
+									size="icon"
 									onClick={handleDelete}
 									title={
 										isOnlyProfile
@@ -264,27 +246,15 @@ const ApiConfigManager = ({
 											: t("settings:providers.deleteProfile")
 									}
 									data-testid="delete-profile-button"
-									disabled={isOnlyProfile}
-									style={{
-										padding: 0,
-										margin: 0,
-										height: "28px",
-										width: "28px",
-										minWidth: "28px",
-									}}>
+									disabled={isOnlyProfile}>
 									<span className="codicon codicon-trash" />
-								</VSCodeButton>
+								</Button>
 							</>
 						)}
 					</div>
-					<p
-						style={{
-							fontSize: "12px",
-							margin: "5px 0 12px",
-							color: "var(--vscode-descriptionForeground)",
-						}}>
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:providers.description")}
-					</p>
+					</div>
 				</>
 			)}
 

+ 7 - 7
webview-ui/src/components/settings/ApiOptions.tsx

@@ -249,7 +249,7 @@ const ApiOptions = ({
 					value={selectedProvider}
 					onValueChange={(value) => setApiConfigurationField("apiProvider", value as ApiProvider)}>
 					<SelectTrigger className="w-full">
-						<SelectValue placeholder="Select" />
+						<SelectValue placeholder={t("settings:common.select")} />
 					</SelectTrigger>
 					<SelectContent>
 						<SelectItem value="openrouter">OpenRouter</SelectItem>
@@ -521,7 +521,7 @@ const ApiOptions = ({
 							value={apiConfiguration?.awsRegion || ""}
 							onValueChange={(value) => setApiConfigurationField("awsRegion", value)}>
 							<SelectTrigger className="w-full">
-								<SelectValue placeholder="Select" />
+								<SelectValue placeholder={t("settings:common.select")} />
 							</SelectTrigger>
 							<SelectContent>
 								{AWS_REGIONS.map(({ value, label }) => (
@@ -595,7 +595,7 @@ const ApiOptions = ({
 							value={apiConfiguration?.vertexRegion || ""}
 							onValueChange={(value) => setApiConfigurationField("vertexRegion", value)}>
 							<SelectTrigger className="w-full">
-								<SelectValue placeholder="Select" />
+								<SelectValue placeholder={t("settings:common.select")} />
 							</SelectTrigger>
 							<SelectContent>
 								{VERTEX_REGIONS.map(({ value, label }) => (
@@ -1222,7 +1222,7 @@ const ApiOptions = ({
 									return { vendor, family }
 								})}>
 								<SelectTrigger className="w-full">
-									<SelectValue placeholder="Select" />
+									<SelectValue placeholder={t("settings:common.select")} />
 								</SelectTrigger>
 								<SelectContent>
 									{vsCodeLmModels.map((model) => (
@@ -1361,7 +1361,7 @@ const ApiOptions = ({
 								setApiConfigurationField("openRouterSpecificProvider", value)
 							}}>
 							<SelectTrigger className="w-full">
-								<SelectValue placeholder="Select" />
+								<SelectValue placeholder={t("settings:common.select")} />
 							</SelectTrigger>
 							<SelectContent>
 								<SelectItem value={OPENROUTER_DEFAULT_PROVIDER_NAME}>
@@ -1374,7 +1374,7 @@ const ApiOptions = ({
 								))}
 							</SelectContent>
 						</Select>
-						<div className="text-sm text-vscode-descriptionForeground">
+						<div className="text-sm text-vscode-descriptionForeground mt-1">
 							{t("settings:providers.openRouter.providerRouting.description")}{" "}
 							<a href="https://openrouter.ai/docs/features/provider-routing">
 								{t("settings:providers.openRouter.providerRouting.learnMore")}.
@@ -1441,7 +1441,7 @@ const ApiOptions = ({
 								}
 							}}>
 							<SelectTrigger className="w-full">
-								<SelectValue placeholder="Select" />
+								<SelectValue placeholder={t("settings:common.select")} />
 							</SelectTrigger>
 							<SelectContent>
 								{selectedProviderModelOptions.map((option) => (

+ 116 - 123
webview-ui/src/components/settings/AutoApproveSettings.tsx

@@ -4,7 +4,7 @@ import { VSCodeButton, VSCodeCheckbox, VSCodeTextField } from "@vscode/webview-u
 import { CheckCheck } from "lucide-react"
 
 import { vscode } from "@/utils/vscode"
-import { ExtensionStateContextType } from "@/context/ExtensionStateContext"
+import { Slider } from "@/components/ui"
 
 import { SetCachedStateField } from "./types"
 import { SectionHeader } from "./SectionHeader"
@@ -22,7 +22,19 @@ type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
 	alwaysAllowSubtasks?: boolean
 	alwaysAllowExecute?: boolean
 	allowedCommands?: string[]
-	setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType>
+	setCachedStateField: SetCachedStateField<
+		| "alwaysAllowReadOnly"
+		| "alwaysAllowWrite"
+		| "writeDelayMs"
+		| "alwaysAllowBrowser"
+		| "alwaysApproveResubmit"
+		| "requestDelaySeconds"
+		| "alwaysAllowMcp"
+		| "alwaysAllowModeSwitch"
+		| "alwaysAllowSubtasks"
+		| "alwaysAllowExecute"
+		| "allowedCommands"
+	>
 }
 
 export const AutoApproveSettings = ({
@@ -71,9 +83,9 @@ export const AutoApproveSettings = ({
 						data-testid="always-allow-readonly-checkbox">
 						<span className="font-medium">{t("settings:autoApprove.readOnly.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:autoApprove.readOnly.description")}
-					</p>
+					</div>
 				</div>
 
 				<div>
@@ -83,35 +95,31 @@ export const AutoApproveSettings = ({
 						data-testid="always-allow-write-checkbox">
 						<span className="font-medium">{t("settings:autoApprove.write.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:autoApprove.write.description")}
-					</p>
-					{alwaysAllowWrite && (
-						<div
-							style={{
-								marginTop: 10,
-								paddingLeft: 10,
-								borderLeft: "2px solid var(--vscode-button-background)",
-							}}>
-							<div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
-								<input
-									type="range"
-									min="0"
-									max="5000"
-									step="100"
-									value={writeDelayMs}
-									onChange={(e) => setCachedStateField("writeDelayMs", parseInt(e.target.value))}
+					</div>
+				</div>
+
+				{alwaysAllowWrite && (
+					<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
+						<div>
+							<div className="flex items-center gap-2">
+								<Slider
+									min={0}
+									max={5000}
+									step={100}
+									value={[writeDelayMs]}
+									onValueChange={([value]) => setCachedStateField("writeDelayMs", value)}
 									data-testid="write-delay-slider"
-									className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
 								/>
-								<span style={{ minWidth: "45px", textAlign: "left" }}>{writeDelayMs}ms</span>
+								<span className="w-20">{writeDelayMs}ms</span>
 							</div>
-							<p className="text-vscode-descriptionForeground text-sm mt-1">
+							<div className="text-vscode-descriptionForeground text-sm mt-1">
 								{t("settings:autoApprove.write.delayLabel")}
-							</p>
+							</div>
 						</div>
-					)}
-				</div>
+					</div>
+				)}
 
 				<div>
 					<VSCodeCheckbox
@@ -120,11 +128,10 @@ export const AutoApproveSettings = ({
 						data-testid="always-allow-browser-checkbox">
 						<span className="font-medium">{t("settings:autoApprove.browser.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
-						{t("settings:autoApprove.browser.description")}
-						<br />
-						{t("settings:autoApprove.browser.note")}
-					</p>
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
+						<div>{t("settings:autoApprove.browser.description")}</div>
+						<div>{t("settings:autoApprove.browser.note")}</div>
+					</div>
 				</div>
 
 				<div>
@@ -134,37 +141,31 @@ export const AutoApproveSettings = ({
 						data-testid="always-approve-resubmit-checkbox">
 						<span className="font-medium">{t("settings:autoApprove.retry.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:autoApprove.retry.description")}
-					</p>
-					{alwaysApproveResubmit && (
-						<div
-							style={{
-								marginTop: 10,
-								paddingLeft: 10,
-								borderLeft: "2px solid var(--vscode-button-background)",
-							}}>
-							<div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
-								<input
-									type="range"
-									min="5"
-									max="100"
-									step="1"
-									value={requestDelaySeconds}
-									onChange={(e) =>
-										setCachedStateField("requestDelaySeconds", parseInt(e.target.value))
-									}
+					</div>
+				</div>
+
+				{alwaysApproveResubmit && (
+					<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
+						<div>
+							<div className="flex items-center gap-2">
+								<Slider
+									min={5}
+									max={100}
+									step={1}
+									value={[requestDelaySeconds]}
+									onValueChange={([value]) => setCachedStateField("requestDelaySeconds", value)}
 									data-testid="request-delay-slider"
-									className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
 								/>
-								<span style={{ minWidth: "45px", textAlign: "left" }}>{requestDelaySeconds}s</span>
+								<span className="w-20">{requestDelaySeconds}s</span>
 							</div>
-							<p className="text-vscode-descriptionForeground text-sm mt-0">
+							<div className="text-vscode-descriptionForeground text-sm mt-1">
 								{t("settings:autoApprove.retry.delayLabel")}
-							</p>
+							</div>
 						</div>
-					)}
-				</div>
+					</div>
+				)}
 
 				<div>
 					<VSCodeCheckbox
@@ -173,9 +174,9 @@ export const AutoApproveSettings = ({
 						data-testid="always-allow-mcp-checkbox">
 						<span className="font-medium">{t("settings:autoApprove.mcp.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:autoApprove.mcp.description")}
-					</p>
+					</div>
 				</div>
 
 				<div>
@@ -185,9 +186,9 @@ export const AutoApproveSettings = ({
 						data-testid="always-allow-mode-switch-checkbox">
 						<span className="font-medium">{t("settings:autoApprove.modeSwitch.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:autoApprove.modeSwitch.description")}
-					</p>
+					</div>
 				</div>
 
 				<div>
@@ -197,9 +198,9 @@ export const AutoApproveSettings = ({
 						data-testid="always-allow-subtasks-checkbox">
 						<span className="font-medium">{t("settings:autoApprove.subtasks.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:autoApprove.subtasks.description")}
-					</p>
+					</div>
 				</div>
 
 				<div>
@@ -209,71 +210,63 @@ export const AutoApproveSettings = ({
 						data-testid="always-allow-execute-checkbox">
 						<span className="font-medium">{t("settings:autoApprove.execute.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:autoApprove.execute.description")}
-					</p>
-					{alwaysAllowExecute && (
-						<div
-							style={{
-								marginTop: 10,
-								paddingLeft: 10,
-								borderLeft: "2px solid var(--vscode-button-background)",
-							}}>
-							<span className="font-medium" data-testid="allowed-commands-heading">
+					</div>
+				</div>
+
+				{alwaysAllowExecute && (
+					<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
+						<div>
+							<label className="block font-medium mb-1" data-testid="allowed-commands-heading">
 								{t("settings:autoApprove.execute.allowedCommands")}
-							</span>
-							<p className="text-vscode-descriptionForeground text-sm mt-0">
+							</label>
+							<div className="text-vscode-descriptionForeground text-sm mt-1">
 								{t("settings:autoApprove.execute.allowedCommandsDescription")}
-							</p>
-							<div style={{ display: "flex", gap: "5px", marginTop: "10px" }}>
-								<VSCodeTextField
-									value={commandInput}
-									onInput={(e: any) => setCommandInput(e.target.value)}
-									onKeyDown={(e: any) => {
-										if (e.key === "Enter") {
-											e.preventDefault()
-											handleAddCommand()
-										}
-									}}
-									placeholder={t("settings:autoApprove.execute.commandPlaceholder")}
-									data-testid="command-input"
-									style={{ flexGrow: 1 }}
-								/>
-								<VSCodeButton onClick={handleAddCommand} data-testid="add-command-button">
-									{t("settings:autoApprove.execute.addButton")}
-								</VSCodeButton>
-							</div>
-							<div
-								style={{
-									marginTop: "10px",
-									display: "flex",
-									flexWrap: "wrap",
-									gap: "5px",
-								}}>
-								{(allowedCommands ?? []).map((cmd, index) => (
-									<div
-										key={index}
-										className="border border-vscode-input-border bg-primary text-primary-foreground flex items-center gap-1 rounded-xs px-1.5 p-0.5">
-										<span>{cmd}</span>
-										<VSCodeButton
-											appearance="icon"
-											className="text-primary-foreground"
-											data-testid={`remove-command-${index}`}
-											onClick={() => {
-												const newCommands = (allowedCommands ?? []).filter(
-													(_, i) => i !== index,
-												)
-												setCachedStateField("allowedCommands", newCommands)
-												vscode.postMessage({ type: "allowedCommands", commands: newCommands })
-											}}>
-											<span className="codicon codicon-close" />
-										</VSCodeButton>
-									</div>
-								))}
 							</div>
 						</div>
-					)}
-				</div>
+
+						<div className="flex gap-2">
+							<VSCodeTextField
+								value={commandInput}
+								onInput={(e: any) => setCommandInput(e.target.value)}
+								onKeyDown={(e: any) => {
+									if (e.key === "Enter") {
+										e.preventDefault()
+										handleAddCommand()
+									}
+								}}
+								placeholder={t("settings:autoApprove.execute.commandPlaceholder")}
+								className="grow"
+								data-testid="command-input"
+							/>
+							<VSCodeButton onClick={handleAddCommand} data-testid="add-command-button">
+								{t("settings:autoApprove.execute.addButton")}
+							</VSCodeButton>
+						</div>
+
+						<div className="flex flex-wrap gap-2">
+							{(allowedCommands ?? []).map((cmd, index) => (
+								<div
+									key={index}
+									className="border border-vscode-input-border bg-primary text-primary-foreground flex items-center gap-1 rounded-xs px-1.5 p-0.5">
+									<span>{cmd}</span>
+									<VSCodeButton
+										appearance="icon"
+										className="text-primary-foreground"
+										data-testid={`remove-command-${index}`}
+										onClick={() => {
+											const newCommands = (allowedCommands ?? []).filter((_, i) => i !== index)
+											setCachedStateField("allowedCommands", newCommands)
+											vscode.postMessage({ type: "allowedCommands", commands: newCommands })
+										}}>
+										<span className="codicon codicon-close" />
+									</VSCodeButton>
+								</div>
+							))}
+						</div>
+					</div>
+				)}
 			</Section>
 		</div>
 	)

+ 100 - 110
webview-ui/src/components/settings/BrowserSettings.tsx

@@ -4,10 +4,9 @@ import { SquareMousePointer } from "lucide-react"
 
 import { vscode } from "@/utils/vscode"
 import { useAppTranslation } from "@/i18n/TranslationContext"
-import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui"
+import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, Slider } from "@/components/ui"
 
 import { SetCachedStateField } from "./types"
-import { sliderLabelStyle } from "./styles"
 import { SectionHeader } from "./SectionHeader"
 import { Section } from "./Section"
 
@@ -128,119 +127,110 @@ export const BrowserSettings = ({
 						onChange={(e: any) => setCachedStateField("browserToolEnabled", e.target.checked)}>
 						<span className="font-medium">{t("settings:browser.enable.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:browser.enable.description")}
-					</p>
-					{browserToolEnabled && (
-						<div
-							style={{
-								marginLeft: 0,
-								paddingLeft: 10,
-								borderLeft: "2px solid var(--vscode-button-background)",
-							}}>
-							<div>
-								<label className="block font-medium mb-1">{t("settings:browser.viewport.label")}</label>
-								<Select
-									value={browserViewportSize}
-									onValueChange={(value) => setCachedStateField("browserViewportSize", value)}>
-									<SelectTrigger className="w-full">
-										<SelectValue placeholder="Select" />
-									</SelectTrigger>
-									<SelectContent>
-										<SelectGroup>
-											{options.map(({ value, label }) => (
-												<SelectItem key={value} value={value}>
-													{label}
-												</SelectItem>
-											))}
-										</SelectGroup>
-									</SelectContent>
-								</Select>
-								<p className="text-vscode-descriptionForeground text-sm mt-1">
-									{t("settings:browser.viewport.description")}
-								</p>
+					</div>
+				</div>
+
+				{browserToolEnabled && (
+					<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
+						<div>
+							<label className="block font-medium mb-1">{t("settings:browser.viewport.label")}</label>
+							<Select
+								value={browserViewportSize}
+								onValueChange={(value) => setCachedStateField("browserViewportSize", value)}>
+								<SelectTrigger className="w-full">
+									<SelectValue placeholder={t("settings:common.select")} />
+								</SelectTrigger>
+								<SelectContent>
+									<SelectGroup>
+										{options.map(({ value, label }) => (
+											<SelectItem key={value} value={value}>
+												{label}
+											</SelectItem>
+										))}
+									</SelectGroup>
+								</SelectContent>
+							</Select>
+							<div className="text-vscode-descriptionForeground text-sm mt-1">
+								{t("settings:browser.viewport.description")}
 							</div>
-							<div>
-								<div style={{ display: "flex", flexDirection: "column", gap: "5px" }}>
-									<span className="font-medium">{t("settings:browser.screenshotQuality.label")}</span>
-									<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
-										<input
-											type="range"
-											min="1"
-											max="100"
-											step="1"
-											value={screenshotQuality ?? 75}
-											className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
-											onChange={(e) =>
-												setCachedStateField("screenshotQuality", parseInt(e.target.value))
-											}
-										/>
-										<span style={{ ...sliderLabelStyle }}>{screenshotQuality ?? 75}%</span>
-									</div>
-								</div>
-								<p className="text-vscode-descriptionForeground text-sm mt-0">
-									{t("settings:browser.screenshotQuality.description")}
-								</p>
+						</div>
+
+						<div>
+							<label className="block font-medium mb-1">
+								{t("settings:browser.screenshotQuality.label")}
+							</label>
+							<div className="flex items-center gap-2">
+								<Slider
+									min={1}
+									max={100}
+									step={1}
+									value={[screenshotQuality ?? 75]}
+									onValueChange={([value]) => setCachedStateField("screenshotQuality", value)}
+								/>
+								<span className="w-10">{screenshotQuality ?? 75}%</span>
 							</div>
-							<div className="mt-4">
-								<div className="mb-2">
-									<VSCodeCheckbox
-										checked={remoteBrowserEnabled}
-										onChange={(e: any) => {
-											// Update the global state - remoteBrowserEnabled now means "enable remote browser connection"
-											setCachedStateField("remoteBrowserEnabled", e.target.checked)
-											if (!e.target.checked) {
-												// If disabling remote browser, clear the custom URL
-												setCachedStateField("remoteBrowserHost", undefined)
-											}
-										}}>
-										<span className="font-medium">{t("settings:browser.remote.label")}</span>
-									</VSCodeCheckbox>
-									<p className="text-vscode-descriptionForeground text-sm mt-0 ml-6">
-										{t("settings:browser.remote.description")}
-									</p>
-								</div>
-								{remoteBrowserEnabled && (
-									<>
-										<div style={{ display: "flex", gap: "5px", marginTop: "10px" }}>
-											<VSCodeTextField
-												value={remoteBrowserHost ?? ""}
-												onChange={(e: any) =>
-													setCachedStateField(
-														"remoteBrowserHost",
-														e.target.value || undefined,
-													)
-												}
-												placeholder={t("settings:browser.remote.urlPlaceholder")}
-												style={{ flexGrow: 1 }}
-											/>
-											<VSCodeButton
-												disabled={testingConnection}
-												onClick={remoteBrowserHost ? testConnection : discoverBrowser}>
-												{testingConnection || discovering
-													? t("settings:browser.remote.testingButton")
-													: t("settings:browser.remote.testButton")}
-											</VSCodeButton>
-										</div>
-										{testResult && (
-											<div
-												className={`p-2 mt-2 mb-2 rounded text-sm ${
-													testResult.success
-														? "bg-green-800/20 text-green-400"
-														: "bg-red-800/20 text-red-400"
-												}`}>
-												{testResult.message}
-											</div>
-										)}
-										<p className="text-vscode-descriptionForeground text-sm mt-2">
-											{t("settings:browser.remote.instructions")}
-										</p>
-									</>
-								)}
+							<div className="text-vscode-descriptionForeground text-sm mt-1">
+								{t("settings:browser.screenshotQuality.description")}
 							</div>
 						</div>
-					)}
-				</div>
+
+						<div>
+							<VSCodeCheckbox
+								checked={remoteBrowserEnabled}
+								onChange={(e: any) => {
+									// Update the global state - remoteBrowserEnabled now means "enable remote browser connection".
+									setCachedStateField("remoteBrowserEnabled", e.target.checked)
+
+									if (!e.target.checked) {
+										// If disabling remote browser, clear the custom URL.
+										setCachedStateField("remoteBrowserHost", undefined)
+									}
+								}}>
+								<label className="block font-medium mb-1">{t("settings:browser.remote.label")}</label>
+							</VSCodeCheckbox>
+							<div className="text-vscode-descriptionForeground text-sm mt-1">
+								{t("settings:browser.remote.description")}
+							</div>
+						</div>
+
+						{remoteBrowserEnabled && (
+							<>
+								<div className="flex items-center gap-2">
+									<VSCodeTextField
+										value={remoteBrowserHost ?? ""}
+										onChange={(e: any) =>
+											setCachedStateField("remoteBrowserHost", e.target.value || undefined)
+										}
+										placeholder={t("settings:browser.remote.urlPlaceholder")}
+										style={{ flexGrow: 1 }}
+									/>
+									<VSCodeButton
+										disabled={testingConnection}
+										onClick={remoteBrowserHost ? testConnection : discoverBrowser}>
+										{testingConnection || discovering
+											? t("settings:browser.remote.testingButton")
+											: t("settings:browser.remote.testButton")}
+									</VSCodeButton>
+								</div>
+								{testResult && (
+									<div
+										className={`p-2 rounded-xs text-sm ${
+											testResult.success
+												? "bg-green-800/20 text-green-400"
+												: "bg-red-800/20 text-red-400"
+										}`}>
+										{testResult.message}
+									</div>
+								)}
+								<div className="text-vscode-descriptionForeground text-sm mt-1">
+									{t("settings:browser.remote.instructions")}
+								</div>
+							</>
+						)}
+					</div>
+				)}
 			</Section>
 		</div>
 	)

+ 35 - 41
webview-ui/src/components/settings/ContextManagementSettings.tsx

@@ -4,9 +4,9 @@ import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
 import { Database } from "lucide-react"
 
 import { cn } from "@/lib/utils"
+import { Slider } from "@/components/ui"
 
 import { SetCachedStateField } from "./types"
-import { sliderLabelStyle } from "./styles"
 import { SectionHeader } from "./SectionHeader"
 import { Section } from "./Section"
 
@@ -37,61 +37,55 @@ export const ContextManagementSettings = ({
 
 			<Section>
 				<div>
-					<div className="flex flex-col gap-2">
-						<span className="font-medium">{t("settings:contextManagement.openTabs.label")}</span>
-						<div className="flex items-center gap-2">
-							<input
-								type="range"
-								min="0"
-								max="500"
-								step="1"
-								value={maxOpenTabsContext ?? 20}
-								onChange={(e) => setCachedStateField("maxOpenTabsContext", parseInt(e.target.value))}
-								className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
-								data-testid="open-tabs-limit-slider"
-							/>
-							<span style={{ ...sliderLabelStyle }}>{maxOpenTabsContext ?? 20}</span>
-						</div>
+					<span className="block font-medium mb-1">{t("settings:contextManagement.openTabs.label")}</span>
+					<div className="flex items-center gap-2">
+						<Slider
+							min={0}
+							max={500}
+							step={1}
+							value={[maxOpenTabsContext ?? 20]}
+							onValueChange={([value]) => setCachedStateField("maxOpenTabsContext", value)}
+							data-testid="open-tabs-limit-slider"
+						/>
+						<span className="w-10">{maxOpenTabsContext ?? 20}</span>
 					</div>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:contextManagement.openTabs.description")}
-					</p>
+					</div>
 				</div>
 
 				<div>
-					<div className="flex flex-col gap-2">
-						<span className="font-medium">{t("settings:contextManagement.workspaceFiles.label")}</span>
-						<div className="flex items-center gap-2">
-							<input
-								type="range"
-								min="0"
-								max="500"
-								step="1"
-								value={maxWorkspaceFiles ?? 200}
-								onChange={(e) => setCachedStateField("maxWorkspaceFiles", parseInt(e.target.value))}
-								className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
-								data-testid="workspace-files-limit-slider"
-							/>
-							<span style={{ ...sliderLabelStyle }}>{maxWorkspaceFiles ?? 200}</span>
-						</div>
+					<span className="block font-medium mb-1">
+						{t("settings:contextManagement.workspaceFiles.label")}
+					</span>
+					<div className="flex items-center gap-2">
+						<Slider
+							min={0}
+							max={500}
+							step={1}
+							value={[maxWorkspaceFiles ?? 200]}
+							onValueChange={([value]) => setCachedStateField("maxWorkspaceFiles", value)}
+							data-testid="workspace-files-limit-slider"
+						/>
+						<span className="w-10">{maxWorkspaceFiles ?? 200}</span>
 					</div>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:contextManagement.workspaceFiles.description")}
-					</p>
+					</div>
 				</div>
 
 				<div>
 					<VSCodeCheckbox
 						checked={showRooIgnoredFiles}
-						onChange={(e: any) => {
-							setCachedStateField("showRooIgnoredFiles", e.target.checked)
-						}}
+						onChange={(e: any) => setCachedStateField("showRooIgnoredFiles", e.target.checked)}
 						data-testid="show-rooignored-files-checkbox">
-						<span className="font-medium">{t("settings:contextManagement.rooignore.label")}</span>
+						<label className="block font-medium mb-1">
+							{t("settings:contextManagement.rooignore.label")}
+						</label>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:contextManagement.rooignore.description")}
-					</p>
+					</div>
 				</div>
 			</Section>
 		</div>

+ 1 - 1
webview-ui/src/components/settings/LanguageSettings.tsx

@@ -47,7 +47,7 @@ export const LanguageSettings = ({ language, setCachedStateField, className, ...
 			<Section>
 				<Select value={language} onValueChange={(value) => setCachedStateField("language", value)}>
 					<SelectTrigger className="w-full">
-						<SelectValue placeholder="Select" />
+						<SelectValue placeholder={t("settings:common.select")} />
 					</SelectTrigger>
 					<SelectContent>
 						<SelectGroup>

+ 4 - 1
webview-ui/src/components/settings/ModelPicker.tsx

@@ -3,6 +3,7 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
 import { Trans } from "react-i18next"
 import { ChevronsUpDown, Check, X } from "lucide-react"
 
+import { useAppTranslation } from "@/i18n/TranslationContext"
 import { cn } from "@/lib/utils"
 import {
 	Command,
@@ -54,6 +55,8 @@ export const ModelPicker = ({
 	setApiConfigurationField,
 	defaultModelInfo,
 }: ModelPickerProps) => {
+	const { t } = useAppTranslation()
+
 	const [open, setOpen] = useState(false)
 	const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
 	const isInitialized = useRef(false)
@@ -117,7 +120,7 @@ export const ModelPicker = ({
 							role="combobox"
 							aria-expanded={open}
 							className="w-full justify-between">
-							<div>{selectedModelId ?? "Select"}</div>
+							<div>{selectedModelId ?? t("settings:common.select")}</div>
 							<ChevronsUpDown className="opacity-50" />
 						</Button>
 					</PopoverTrigger>

+ 42 - 42
webview-ui/src/components/settings/NotificationSettings.tsx

@@ -6,6 +6,7 @@ import { Bell } from "lucide-react"
 import { SetCachedStateField } from "./types"
 import { SectionHeader } from "./SectionHeader"
 import { Section } from "./Section"
+import { Slider } from "../ui"
 
 type NotificationSettingsProps = HTMLAttributes<HTMLDivElement> & {
 	ttsEnabled?: boolean
@@ -41,31 +42,32 @@ export const NotificationSettings = ({
 						data-testid="tts-enabled-checkbox">
 						<span className="font-medium">{t("settings:notifications.tts.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:notifications.tts.description")}
-					</p>
-					{ttsEnabled && (
-						<div className="pl-[10px] ml-0 border-l-2 border-l-vscode-button-background">
-							<div className="flex items-center gap-[5px]">
-								<input
-									type="range"
-									min="0.1"
-									max="2.0"
-									step="0.01"
-									value={ttsSpeed ?? 1.0}
-									onChange={(e) => setCachedStateField("ttsSpeed", parseFloat(e.target.value))}
-									className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
-									aria-label="Speed"
+					</div>
+				</div>
+
+				{ttsEnabled && (
+					<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
+						<div>
+							<label className="block font-medium mb-1">
+								{t("settings:notifications.tts.speedLabel")}
+							</label>
+							<div className="flex items-center gap-2">
+								<Slider
+									min={0.1}
+									max={2.0}
+									step={0.01}
+									value={[ttsSpeed ?? 1.0]}
+									onValueChange={([value]) => setCachedStateField("ttsSpeed", value)}
 									data-testid="tts-speed-slider"
 								/>
-								<span className="min-w-[35px] text-left">{((ttsSpeed ?? 1.0) * 100).toFixed(0)}%</span>
+								<span className="w-10">{((ttsSpeed ?? 1.0) * 100).toFixed(0)}%</span>
 							</div>
-							<p className="text-vscode-descriptionForeground text-sm mt-1">
-								{t("settings:notifications.tts.speedLabel")}
-							</p>
 						</div>
-					)}
-				</div>
+					</div>
+				)}
+
 				<div>
 					<VSCodeCheckbox
 						checked={soundEnabled}
@@ -73,33 +75,31 @@ export const NotificationSettings = ({
 						data-testid="sound-enabled-checkbox">
 						<span className="font-medium">{t("settings:notifications.sound.label")}</span>
 					</VSCodeCheckbox>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:notifications.sound.description")}
-					</p>
-					{soundEnabled && (
-						<div className="pl-[10px] ml-0 border-l-2 border-l-vscode-button-background">
-							<div className="flex items-center gap-[5px]">
-								<input
-									type="range"
-									min="0"
-									max="1"
-									step="0.01"
-									value={soundVolume ?? 0.5}
-									onChange={(e) => setCachedStateField("soundVolume", parseFloat(e.target.value))}
-									className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
-									aria-label="Volume"
+					</div>
+				</div>
+
+				{soundEnabled && (
+					<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
+						<div>
+							<label className="block font-medium mb-1">
+								{t("settings:notifications.sound.volumeLabel")}
+							</label>
+							<div className="flex items-center gap-2">
+								<Slider
+									min={0}
+									max={1}
+									step={0.01}
+									value={[soundVolume ?? 0.5]}
+									onValueChange={([value]) => setCachedStateField("soundVolume", value)}
 									data-testid="sound-volume-slider"
 								/>
-								<span className="min-w-[35px] text-left">
-									{((soundVolume ?? 0.5) * 100).toFixed(0)}%
-								</span>
+								<span className="w-10">{((soundVolume ?? 0.5) * 100).toFixed(0)}%</span>
 							</div>
-							<p className="text-vscode-descriptionForeground text-sm mt-1">
-								{t("settings:notifications.sound.volumeLabel")}
-							</p>
 						</div>
-					)}
-				</div>
+					</div>
+				)}
 			</Section>
 		</div>
 	)

+ 1 - 1
webview-ui/src/components/settings/Section.tsx

@@ -5,5 +5,5 @@ import { cn } from "@/lib/utils"
 type SectionProps = HTMLAttributes<HTMLDivElement>
 
 export const Section = ({ className, ...props }: SectionProps) => (
-	<div className={cn("flex flex-col gap-2 p-5", className)} {...props} />
+	<div className={cn("flex flex-col gap-3 p-5", className)} {...props} />
 )

+ 0 - 1
webview-ui/src/components/settings/SettingsView.tsx

@@ -466,7 +466,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
 				<div ref={advancedRef}>
 					<AdvancedSettings
 						rateLimitSeconds={rateLimitSeconds}
-						terminalShellIntegrationTimeout={terminalShellIntegrationTimeout}
 						diffEnabled={diffEnabled}
 						fuzzyMatchThreshold={fuzzyMatchThreshold}
 						setCachedStateField={setCachedStateField}

+ 28 - 26
webview-ui/src/components/settings/TemperatureControl.tsx

@@ -3,18 +3,22 @@ import { useEffect, useState } from "react"
 import { useAppTranslation } from "@/i18n/TranslationContext"
 import { useDebounce } from "react-use"
 
+import { Slider } from "@/components/ui"
+
 interface TemperatureControlProps {
 	value: number | undefined | null
 	onChange: (value: number | undefined | null) => void
-	maxValue?: number // Some providers like OpenAI use 0-2 range
+	maxValue?: number // Some providers like OpenAI use 0-2 range.
 }
 
 export const TemperatureControl = ({ value, onChange, maxValue = 1 }: TemperatureControlProps) => {
 	const { t } = useAppTranslation()
 	const [isCustomTemperature, setIsCustomTemperature] = useState(value !== undefined)
 	const [inputValue, setInputValue] = useState(value)
+
 	useDebounce(() => onChange(inputValue), 50, [onChange, inputValue])
-	// Sync internal state with prop changes when switching profiles
+
+	// Sync internal state with prop changes when switching profiles.
 	useEffect(() => {
 		const hasCustomTemperature = value !== undefined && value !== null
 		setIsCustomTemperature(hasCustomTemperature)
@@ -29,39 +33,37 @@ export const TemperatureControl = ({ value, onChange, maxValue = 1 }: Temperatur
 					onChange={(e: any) => {
 						const isChecked = e.target.checked
 						setIsCustomTemperature(isChecked)
+
 						if (!isChecked) {
-							setInputValue(null) // Unset the temperature, note that undefined is unserializable
+							setInputValue(null) // Unset the temperature, note that undefined is unserializable.
 						} else {
-							setInputValue(value ?? 0) // Use the value from apiConfiguration, if set
+							setInputValue(value ?? 0) // Use the value from apiConfiguration, if set.
 						}
 					}}>
-					<span className="font-medium">{t("settings:temperature.useCustom")}</span>
+					<label className="block font-medium mb-1">{t("settings:temperature.useCustom")}</label>
 				</VSCodeCheckbox>
-				<div className="text-sm text-vscode-descriptionForeground">{t("settings:temperature.description")}</div>
+				<div className="text-sm text-vscode-descriptionForeground mt-1">
+					{t("settings:temperature.description")}
+				</div>
 			</div>
 
 			{isCustomTemperature && (
-				<div
-					style={{
-						marginLeft: 0,
-						paddingLeft: 10,
-						borderLeft: "2px solid var(--vscode-button-background)",
-					}}>
-					<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
-						<input
-							type="range"
-							min="0"
-							max={maxValue}
-							step="0.01"
-							value={inputValue ?? 0}
-							className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
-							onChange={(e) => setInputValue(parseFloat(e.target.value))}
-						/>
-						<span>{inputValue}</span>
+				<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
+					<div>
+						<div className="flex items-center gap-2">
+							<Slider
+								min={0}
+								max={maxValue}
+								step={0.01}
+								value={[inputValue ?? 0]}
+								onValueChange={([value]) => setInputValue(value)}
+							/>
+							<span className="w-10">{inputValue}</span>
+						</div>
+						<div className="text-vscode-descriptionForeground text-sm mt-1">
+							{t("settings:temperature.rangeDescription")}
+						</div>
 					</div>
-					<p className="text-vscode-descriptionForeground text-sm mt-1">
-						{t("settings:temperature.rangeDescription")}
-					</p>
 				</div>
 			)}
 		</>

+ 34 - 44
webview-ui/src/components/settings/TerminalSettings.tsx

@@ -3,9 +3,9 @@ import { useAppTranslation } from "@/i18n/TranslationContext"
 import { SquareTerminal } from "lucide-react"
 
 import { cn } from "@/lib/utils"
+import { Slider } from "@/components/ui"
 
 import { SetCachedStateField } from "./types"
-import { sliderLabelStyle } from "./styles"
 import { SectionHeader } from "./SectionHeader"
 import { Section } from "./Section"
 
@@ -35,54 +35,44 @@ export const TerminalSettings = ({
 
 			<Section>
 				<div>
-					<div className="flex flex-col gap-2">
-						<span className="font-medium">{t("settings:terminal.outputLineLimit.label")}</span>
-						<div className="flex items-center gap-2">
-							<input
-								type="range"
-								min="100"
-								max="5000"
-								step="100"
-								value={terminalOutputLineLimit ?? 500}
-								onChange={(e) =>
-									setCachedStateField("terminalOutputLineLimit", parseInt(e.target.value))
-								}
-								className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
-								data-testid="terminal-output-limit-slider"
-							/>
-							<span style={{ ...sliderLabelStyle }}>{terminalOutputLineLimit ?? 500}</span>
-						</div>
+					<label className="block font-medium mb-1">{t("settings:terminal.outputLineLimit.label")}</label>
+					<div className="flex items-center gap-2">
+						<Slider
+							min={100}
+							max={5000}
+							step={100}
+							value={[terminalOutputLineLimit ?? 500]}
+							onValueChange={([value]) => setCachedStateField("terminalOutputLineLimit", value)}
+							data-testid="terminal-output-limit-slider"
+						/>
+						<span className="w-10">{terminalOutputLineLimit ?? 500}</span>
 					</div>
-					<p className="text-vscode-descriptionForeground text-sm mt-0">
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
 						{t("settings:terminal.outputLineLimit.description")}
-					</p>
+					</div>
 				</div>
 
 				<div>
-					<div className="flex flex-col gap-2">
-						<span className="font-medium">{t("settings:terminal.shellIntegrationTimeout.label")}</span>
-						<div className="flex items-center gap-2">
-							<input
-								type="range"
-								min="1000"
-								max="60000"
-								step="1000"
-								value={terminalShellIntegrationTimeout}
-								onChange={(e) =>
-									setCachedStateField(
-										"terminalShellIntegrationTimeout",
-										Math.min(60000, Math.max(1000, parseInt(e.target.value))),
-									)
-								}
-								className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
-							/>
-							<span style={{ ...sliderLabelStyle }}>
-								{(terminalShellIntegrationTimeout ?? 5000) / 1000}s
-							</span>
-						</div>
-						<p className="text-vscode-descriptionForeground text-sm mt-0">
-							{t("settings:terminal.shellIntegrationTimeout.description")}
-						</p>
+					<label className="block font-medium mb-1">
+						{t("settings:terminal.shellIntegrationTimeout.label")}
+					</label>
+					<div className="flex items-center gap-2">
+						<Slider
+							min={1000}
+							max={60000}
+							step={1000}
+							value={[terminalShellIntegrationTimeout ?? 5000]}
+							onValueChange={([value]) =>
+								setCachedStateField(
+									"terminalShellIntegrationTimeout",
+									Math.min(60000, Math.max(1000, value)),
+								)
+							}
+						/>
+						<span className="w-10">{(terminalShellIntegrationTimeout ?? 5000) / 1000}s</span>
+					</div>
+					<div className="text-vscode-descriptionForeground text-sm mt-1">
+						{t("settings:terminal.shellIntegrationTimeout.description")}
 					</div>
 				</div>
 			</Section>

+ 26 - 26
webview-ui/src/components/settings/__tests__/ApiConfigManager.test.tsx

@@ -1,13 +1,12 @@
+// npx jest src/components/settings/__tests__/ApiConfigManager.test.tsx
+
+import React from "react"
 import { render, screen, fireEvent, within } from "@testing-library/react"
+
 import ApiConfigManager from "../ApiConfigManager"
 
 // Mock VSCode components
 jest.mock("@vscode/webview-ui-toolkit/react", () => ({
-	VSCodeButton: ({ children, onClick, title, disabled, "data-testid": dataTestId }: any) => (
-		<button onClick={onClick} title={title} disabled={disabled} data-testid={dataTestId}>
-			{children}
-		</button>
-	),
 	VSCodeTextField: ({ value, onInput, placeholder, onKeyDown, "data-testid": dataTestId }: any) => (
 		<input
 			value={value}
@@ -20,22 +19,8 @@ jest.mock("@vscode/webview-ui-toolkit/react", () => ({
 	),
 }))
 
-jest.mock("vscrui", () => ({
-	Dropdown: ({ id, value, onChange, options, role }: any) => (
-		<div data-testid={`mock-dropdown-${id}`}>
-			<select value={value} onChange={(e) => onChange({ value: e.target.value })} data-testid={id} role={role}>
-				{options.map((opt: any) => (
-					<option key={opt.value} value={opt.value}>
-						{opt.label}
-					</option>
-				))}
-			</select>
-		</div>
-	),
-}))
-
-// Mock Dialog component
-jest.mock("@/components/ui/dialog", () => ({
+jest.mock("@/components/ui", () => ({
+	...jest.requireActual("@/components/ui"),
 	Dialog: ({ children, open, onOpenChange }: any) => (
 		<div role="dialog" aria-modal="true" style={{ display: open ? "block" : "none" }} data-testid="dialog">
 			{children}
@@ -43,10 +28,6 @@ jest.mock("@/components/ui/dialog", () => ({
 	),
 	DialogContent: ({ children }: any) => <div data-testid="dialog-content">{children}</div>,
 	DialogTitle: ({ children }: any) => <div data-testid="dialog-title">{children}</div>,
-}))
-
-// Mock UI components
-jest.mock("@/components/ui", () => ({
 	Button: ({ children, onClick, disabled, variant, "data-testid": dataTestId }: any) => (
 		<button onClick={onClick} disabled={disabled} data-testid={dataTestId}>
 			{children}
@@ -61,6 +42,25 @@ jest.mock("@/components/ui", () => ({
 			data-testid={dataTestId}
 		/>
 	),
+	Select: ({ children, value, onValueChange }: any) => (
+		<select
+			value={value}
+			onChange={(e) => {
+				if (onValueChange) onValueChange(e.target.value)
+			}}
+			data-testid="select-component">
+			<option value="Default Config">Default Config</option>
+			<option value="Another Config">Another Config</option>
+		</select>
+	),
+	SelectTrigger: ({ children }: any) => <div className="select-trigger-mock">{children}</div>,
+	SelectValue: ({ children }: any) => <div className="select-value-mock">{children}</div>,
+	SelectContent: ({ children }: any) => <div className="select-content-mock">{children}</div>,
+	SelectItem: ({ children, value }: any) => (
+		<option value={value} className="select-item-mock">
+			{children}
+		</option>
+	),
 }))
 
 describe("ApiConfigManager", () => {
@@ -215,7 +215,7 @@ describe("ApiConfigManager", () => {
 	it("allows selecting a different config", () => {
 		render(<ApiConfigManager {...defaultProps} />)
 
-		const select = screen.getByRole("combobox")
+		const select = screen.getByTestId("select-component")
 		fireEvent.change(select, { target: { value: "Another Config" } })
 
 		expect(mockOnSelectConfig).toHaveBeenCalledWith("Another Config")

+ 23 - 3
webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx

@@ -1,9 +1,31 @@
+// npx jest src/components/settings/__tests__/ContextManagementSettings.test.ts
+
 import { render, screen, fireEvent } from "@testing-library/react"
+
 import { ContextManagementSettings } from "../ContextManagementSettings"
 
+class MockResizeObserver {
+	observe() {}
+	unobserve() {}
+	disconnect() {}
+}
+
+global.ResizeObserver = MockResizeObserver
+
+jest.mock("@/components/ui", () => ({
+	...jest.requireActual("@/components/ui"),
+	Slider: ({ value, onValueChange, "data-testid": dataTestId }: any) => (
+		<input
+			type="range"
+			value={value[0]}
+			onChange={(e) => onValueChange([parseFloat(e.target.value)])}
+			data-testid={dataTestId}
+		/>
+	),
+}))
+
 describe("ContextManagementSettings", () => {
 	const defaultProps = {
-		terminalOutputLineLimit: 500,
 		maxOpenTabsContext: 20,
 		maxWorkspaceFiles: 200,
 		showRooIgnoredFiles: false,
@@ -20,12 +42,10 @@ describe("ContextManagementSettings", () => {
 		// Open tabs context limit
 		const openTabsSlider = screen.getByTestId("open-tabs-limit-slider")
 		expect(openTabsSlider).toBeInTheDocument()
-		expect(openTabsSlider).toHaveValue("20")
 
 		// Workspace files limit
 		const workspaceFilesSlider = screen.getByTestId("workspace-files-limit-slider")
 		expect(workspaceFilesSlider).toBeInTheDocument()
-		expect(workspaceFilesSlider).toHaveValue("200")
 
 		// Show .rooignore'd files
 		const showRooIgnoredFilesCheckbox = screen.getByTestId("show-rooignored-files-checkbox")

+ 16 - 14
webview-ui/src/components/settings/__tests__/SettingsView.test.tsx

@@ -78,28 +78,22 @@ jest.mock("@vscode/webview-ui-toolkit/react", () => ({
 			data-testid={dataTestId}
 		/>
 	),
-	VSCodeTextArea: () => <textarea />,
 	VSCodeLink: ({ children, href }: any) => <a href={href || "#"}>{children}</a>,
-	VSCodeDropdown: ({ children, value, onChange }: any) => (
-		<select value={value} onChange={onChange}>
-			{children}
-		</select>
-	),
-	VSCodeOption: ({ children, value }: any) => <option value={value}>{children}</option>,
 	VSCodeRadio: ({ children, value, checked, onChange }: any) => (
 		<input type="radio" value={value} checked={checked} onChange={onChange} />
 	),
 	VSCodeRadioGroup: ({ children, value, onChange }: any) => <div onChange={onChange}>{children}</div>,
-	VSCodeSlider: ({ value, onChange, "data-testid": dataTestId }: any) => (
+}))
+
+// Mock Slider component
+jest.mock("@/components/ui", () => ({
+	...jest.requireActual("@/components/ui"),
+	Slider: ({ value, onValueChange, "data-testid": dataTestId }: any) => (
 		<input
 			type="range"
-			value={value}
-			onChange={(e) => onChange({ target: { value: Number(e.target.value) } })}
-			min={0}
-			max={1}
+			value={value[0]}
+			onChange={(e) => onValueChange([parseFloat(e.target.value)])}
 			data-testid={dataTestId}
-			step={0.01}
-			style={{ flexGrow: 1, height: "2px" }}
 		/>
 	),
 }))
@@ -127,6 +121,14 @@ const mockPostMessage = (state: any) => {
 	)
 }
 
+class MockResizeObserver {
+	observe() {}
+	unobserve() {}
+	disconnect() {}
+}
+
+global.ResizeObserver = MockResizeObserver
+
 const renderSettingsView = () => {
 	const onDone = jest.fn()
 	const queryClient = new QueryClient()

+ 32 - 42
webview-ui/src/components/settings/__tests__/TemperatureControl.test.tsx

@@ -1,6 +1,29 @@
+// npx jest src/components/settings/__tests__/TemperatureControl.test.ts
+
 import { render, screen, fireEvent } from "@testing-library/react"
+
 import { TemperatureControl } from "../TemperatureControl"
 
+class MockResizeObserver {
+	observe() {}
+	unobserve() {}
+	disconnect() {}
+}
+
+global.ResizeObserver = MockResizeObserver
+
+jest.mock("@/components/ui", () => ({
+	...jest.requireActual("@/components/ui"),
+	Slider: ({ value, onValueChange, "data-testid": dataTestId }: any) => (
+		<input
+			type="range"
+			value={value[0]}
+			onChange={(e) => onValueChange([parseFloat(e.target.value)])}
+			data-testid={dataTestId}
+		/>
+	),
+}))
+
 describe("TemperatureControl", () => {
 	it("renders with default temperature disabled", () => {
 		const onChange = jest.fn()
@@ -29,67 +52,34 @@ describe("TemperatureControl", () => {
 
 		const checkbox = screen.getByRole("checkbox")
 
-		// Uncheck - should clear temperature
+		// Uncheck - should clear temperature.
 		fireEvent.click(checkbox)
-		// Waiting for debounce
+
+		// Waiting for debounce.
 		await new Promise((x) => setTimeout(x, 100))
 		expect(onChange).toHaveBeenCalledWith(null)
 
-		// Check - should restore previous temperature
+		// Check - should restore previous temperature.
 		fireEvent.click(checkbox)
-		// Waiting for debounce
-		await new Promise((x) => setTimeout(x, 100))
-		expect(onChange).toHaveBeenCalledWith(0.7)
-	})
-
-	it("updates temperature when input loses focus", async () => {
-		const onChange = jest.fn()
-		render(<TemperatureControl value={0.7} onChange={onChange} />)
-
-		const input = screen.getByRole("slider")
-		fireEvent.change(input, { target: { value: "0.8" } })
-		fireEvent.blur(input)
 
-		// Waiting for debounce
+		// Waiting for debounce.
 		await new Promise((x) => setTimeout(x, 100))
-		expect(onChange).toHaveBeenCalledWith(0.8)
-	})
-
-	it("respects maxValue prop", async () => {
-		const onChange = jest.fn()
-		render(<TemperatureControl value={1.5} onChange={onChange} maxValue={2} />)
-
-		const input = screen.getByRole("slider")
-
-		// Valid value within max
-		fireEvent.change(input, { target: { value: "1.8" } })
-		fireEvent.blur(input)
-		// Waiting for debounce
-		await new Promise((x) => setTimeout(x, 100))
-		expect(onChange).toHaveBeenCalledWith(1.8)
-
-		// Invalid value above max
-		fireEvent.change(input, { target: { value: "2.5" } })
-		fireEvent.blur(input)
-		expect(input).toHaveValue("2") // Clamped between 0 and 2
-		// Waiting for debounce
-		await new Promise((x) => setTimeout(x, 100))
-		expect(onChange).toHaveBeenCalledWith(2)
+		expect(onChange).toHaveBeenCalledWith(0.7)
 	})
 
 	it("syncs checkbox state when value prop changes", () => {
 		const onChange = jest.fn()
 		const { rerender } = render(<TemperatureControl value={0.7} onChange={onChange} />)
 
-		// Initially checked
+		// Initially checked.
 		const checkbox = screen.getByRole("checkbox")
 		expect(checkbox).toBeChecked()
 
-		// Update to undefined
+		// Update to undefined.
 		rerender(<TemperatureControl value={undefined} onChange={onChange} />)
 		expect(checkbox).not.toBeChecked()
 
-		// Update back to a value
+		// Update back to a value.
 		rerender(<TemperatureControl value={0.5} onChange={onChange} />)
 		expect(checkbox).toBeChecked()
 	})

+ 0 - 1
webview-ui/src/components/settings/__tests__/ThinkingBudget.test.tsx

@@ -2,7 +2,6 @@ import { render, screen, fireEvent } from "@testing-library/react"
 import { ThinkingBudget } from "../ThinkingBudget"
 import { ModelInfo } from "../../../../../src/shared/api"
 
-// Mock Slider component
 jest.mock("@/components/ui", () => ({
 	Slider: ({ value, onValueChange, min, max }: any) => (
 		<input

+ 0 - 7
webview-ui/src/components/settings/styles.ts

@@ -76,10 +76,3 @@ export const StyledMarkdown = styled.div`
 		}
 	}
 `
-
-export const sliderLabelStyle = {
-	minWidth: "45px",
-	textAlign: "right" as const,
-	lineHeight: "20px",
-	paddingBottom: "2px",
-}

+ 7 - 1
webview-ui/src/components/ui/__tests__/select-dropdown.test.tsx

@@ -1,4 +1,6 @@
-import React, { ReactNode } from "react"
+// npx jest webview-ui/src/components/ui/__tests__/select-dropdown.test.tsx
+
+import { ReactNode } from "react"
 import { render, screen, fireEvent } from "@testing-library/react"
 import { SelectDropdown, DropdownOptionType } from "../select-dropdown"
 
@@ -47,6 +49,10 @@ jest.mock("../dropdown-menu", () => {
 		),
 
 		DropdownMenuSeparator: () => <div data-testid="dropdown-separator" />,
+
+		DropdownMenuShortcut: ({ children }: { children: ReactNode }) => (
+			<span data-testid="dropdown-shortcut">{children}</span>
+		),
 	}
 })
 

+ 8 - 6
webview-ui/src/components/ui/select-dropdown.tsx

@@ -9,7 +9,9 @@ import {
 	DropdownMenuItem,
 	DropdownMenuTrigger,
 	DropdownMenuSeparator,
+	DropdownMenuShortcut,
 } from "./dropdown-menu"
+import { Check } from "lucide-react"
 
 export enum DropdownOptionType {
 	ITEM = "item",
@@ -30,7 +32,6 @@ export interface SelectDropdownProps {
 	onChange: (value: string) => void
 	disabled?: boolean
 	title?: string
-	className?: string
 	triggerClassName?: string
 	contentClassName?: string
 	sideOffset?: number
@@ -48,7 +49,6 @@ export const SelectDropdown = React.forwardRef<React.ElementRef<typeof DropdownM
 			onChange,
 			disabled = false,
 			title = "",
-			className = "",
 			triggerClassName = "",
 			contentClassName = "",
 			sideOffset = 4,
@@ -141,12 +141,14 @@ export const SelectDropdown = React.forwardRef<React.ElementRef<typeof DropdownM
 							<DropdownMenuItem
 								key={`item-${option.value}`}
 								disabled={option.disabled}
-								className={cn(
-									"cursor-pointer text-xs focus:bg-vscode-list-hoverBackground focus:text-vscode-list-hoverForeground",
-									option.value === value && "bg-vscode-list-focusBackground",
-								)}
+								className="text-xs focus:bg-vscode-list-activeSelectionBackground cursor-pointer"
 								onClick={() => handleSelect(option)}>
 								{option.label}
+								{option.value === value && (
+									<DropdownMenuShortcut>
+										<Check className="w-3 h-3" />
+									</DropdownMenuShortcut>
+								)}
 							</DropdownMenuItem>
 						)
 					})}

+ 1 - 1
webview-ui/src/components/ui/slider.tsx

@@ -11,7 +11,7 @@ const Slider = React.forwardRef<
 		ref={ref}
 		className={cn("relative flex w-full touch-none select-none items-center", className)}
 		{...props}>
-		<SliderPrimitive.Track className="relative w-full h-[8px] grow overflow-hidden bg-vscode-button-secondaryBackground border border-[#767676] dark:border-[#858585] rounded-sm">
+		<SliderPrimitive.Track className="relative w-full h-[8px] grow overflow-hidden bg-accent border border-[#767676] dark:border-[#858585] rounded-sm">
 			<SliderPrimitive.Range className="absolute h-full bg-vscode-button-background" />
 		</SliderPrimitive.Track>
 		<SliderPrimitive.Thumb className="block h-3 w-3 rounded-full border border-primary/50 bg-primary shadow transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />

+ 3 - 2
webview-ui/src/i18n/locales/ca/settings.json

@@ -3,7 +3,8 @@
 		"save": "Desar",
 		"done": "Fet",
 		"cancel": "Cancel·lar",
-		"reset": "Restablir"
+		"reset": "Restablir",
+		"select": "Seleccionar"
 	},
 	"header": {
 		"title": "Configuració",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "Perfil de configuració",
-		"description": "Descripció",
+		"description": "Deseu diferents configuracions d'API per canviar ràpidament entre proveïdors i configuracions.",
 		"apiProvider": "Proveïdor d'API",
 		"openRouterApiKey": "Clau API d'OpenRouter",
 		"nameEmpty": "El nom no pot estar buit",

+ 3 - 2
webview-ui/src/i18n/locales/de/settings.json

@@ -3,7 +3,8 @@
 		"save": "Speichern",
 		"done": "Fertig",
 		"cancel": "Abbrechen",
-		"reset": "Zurücksetzen"
+		"reset": "Zurücksetzen",
+		"select": "Auswählen"
 	},
 	"header": {
 		"title": "Einstellungen",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "Konfigurationsprofil",
-		"description": "Beschreibung",
+		"description": "Speichern Sie verschiedene API-Konfigurationen, um schnell zwischen Anbietern und Einstellungen zu wechseln.",
 		"apiProvider": "API-Anbieter",
 		"model": "Modell",
 		"nameEmpty": "Name darf nicht leer sein",

+ 3 - 2
webview-ui/src/i18n/locales/en/settings.json

@@ -3,7 +3,8 @@
 		"save": "Save",
 		"done": "Done",
 		"cancel": "Cancel",
-		"reset": "Reset"
+		"reset": "Reset",
+		"select": "Select"
 	},
 	"header": {
 		"title": "Settings",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "Configuration Profile",
-		"description": "Description",
+		"description": "Save different API configurations to quickly switch between providers and settings.",
 		"apiProvider": "API Provider",
 		"model": "Model",
 		"nameEmpty": "Name cannot be empty",

+ 3 - 2
webview-ui/src/i18n/locales/es/settings.json

@@ -3,7 +3,8 @@
 		"save": "Guardar",
 		"done": "Hecho",
 		"cancel": "Cancelar",
-		"reset": "Restablecer"
+		"reset": "Restablecer",
+		"select": "Seleccionar"
 	},
 	"header": {
 		"title": "Configuración",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "Perfil de configuración",
-		"description": "Descripción",
+		"description": "Guarde diferentes configuraciones de API para cambiar rápidamente entre proveedores y ajustes.",
 		"apiProvider": "Proveedor de API",
 		"model": "Modelo",
 		"nameEmpty": "El nombre no puede estar vacío",

+ 3 - 2
webview-ui/src/i18n/locales/fr/settings.json

@@ -3,7 +3,8 @@
 		"save": "Enregistrer",
 		"done": "Terminé",
 		"cancel": "Annuler",
-		"reset": "Réinitialiser"
+		"reset": "Réinitialiser",
+		"select": "Sélectionner"
 	},
 	"header": {
 		"title": "Paramètres",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "Profil de configuration",
-		"description": "Description",
+		"description": "Enregistrez différentes configurations d'API pour basculer rapidement entre les fournisseurs et les paramètres.",
 		"apiProvider": "Fournisseur d'API",
 		"model": "Modèle",
 		"nameEmpty": "Le nom ne peut pas être vide",

+ 3 - 2
webview-ui/src/i18n/locales/hi/settings.json

@@ -3,7 +3,8 @@
 		"save": "सहेजें",
 		"done": "पूर्ण",
 		"cancel": "रद्द करें",
-		"reset": "रीसेट करें"
+		"reset": "रीसेट करें",
+		"select": "चुनें"
 	},
 	"header": {
 		"title": "सेटिंग्स",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "कॉन्फिगरेशन प्रोफाइल",
-		"description": "विवरण",
+		"description": "विभिन्न API कॉन्फ़िगरेशन सहेजें ताकि प्रदाताओं और सेटिंग्स के बीच त्वरित रूप से स्विच कर सकें।",
 		"apiProvider": "API प्रदाता",
 		"model": "मॉडल",
 		"nameEmpty": "नाम खाली नहीं हो सकता",

+ 3 - 2
webview-ui/src/i18n/locales/it/settings.json

@@ -3,7 +3,8 @@
 		"save": "Salva",
 		"done": "Fatto",
 		"cancel": "Annulla",
-		"reset": "Ripristina"
+		"reset": "Ripristina",
+		"select": "Seleziona"
 	},
 	"header": {
 		"title": "Impostazioni",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "Profilo di configurazione",
-		"description": "Descrizione",
+		"description": "Salva diverse configurazioni API per passare rapidamente tra fornitori e impostazioni.",
 		"apiProvider": "Fornitore API",
 		"model": "Modello",
 		"nameEmpty": "Il nome non può essere vuoto",

+ 3 - 2
webview-ui/src/i18n/locales/ja/settings.json

@@ -3,7 +3,8 @@
 		"save": "保存",
 		"done": "完了",
 		"cancel": "キャンセル",
-		"reset": "リセット"
+		"reset": "リセット",
+		"select": "選択"
 	},
 	"header": {
 		"title": "設定",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "設定プロファイル",
-		"description": "説明",
+		"description": "異なるAPI設定を保存して、プロバイダーと設定をすばやく切り替えることができます。",
 		"apiProvider": "APIプロバイダー",
 		"model": "モデル",
 		"nameEmpty": "名前を空にすることはできません",

+ 3 - 2
webview-ui/src/i18n/locales/ko/settings.json

@@ -3,7 +3,8 @@
 		"save": "저장",
 		"done": "완료",
 		"cancel": "취소",
-		"reset": "초기화"
+		"reset": "초기화",
+		"select": "선택"
 	},
 	"header": {
 		"title": "설정",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "구성 프로필",
-		"description": "설명",
+		"description": "다양한 API 구성을 저장하여 제공자와 설정 간에 빠르게 전환할 수 있습니다.",
 		"apiProvider": "API 제공자",
 		"model": "모델",
 		"nameEmpty": "이름은 비워둘 수 없습니다",

+ 3 - 2
webview-ui/src/i18n/locales/pl/settings.json

@@ -3,7 +3,8 @@
 		"save": "Zapisz",
 		"done": "Gotowe",
 		"cancel": "Anuluj",
-		"reset": "Resetuj"
+		"reset": "Resetuj",
+		"select": "Wybierz"
 	},
 	"header": {
 		"title": "Ustawienia",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "Profil konfiguracji",
-		"description": "Opis",
+		"description": "Zapisz różne konfiguracje API, aby szybko przełączać się między dostawcami i ustawieniami.",
 		"apiProvider": "Dostawca API",
 		"model": "Model",
 		"nameEmpty": "Nazwa nie może być pusta",

+ 3 - 2
webview-ui/src/i18n/locales/pt-BR/settings.json

@@ -3,7 +3,8 @@
 		"save": "Salvar",
 		"done": "Concluído",
 		"cancel": "Cancelar",
-		"reset": "Redefinir"
+		"reset": "Redefinir",
+		"select": "Selecionar"
 	},
 	"header": {
 		"title": "Configurações",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "Perfil de configuração",
-		"description": "Descrição",
+		"description": "Salve diferentes configurações de API para alternar rapidamente entre provedores e configurações.",
 		"apiProvider": "Provedor de API",
 		"model": "Modelo",
 		"nameEmpty": "O nome não pode estar vazio",

+ 3 - 2
webview-ui/src/i18n/locales/tr/settings.json

@@ -3,7 +3,8 @@
 		"save": "Kaydet",
 		"done": "Tamamlandı",
 		"cancel": "İptal",
-		"reset": "Sıfırla"
+		"reset": "Sıfırla",
+		"select": "Seç"
 	},
 	"header": {
 		"title": "Ayarlar",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "Yapılandırma Profili",
-		"description": "Açıklama",
+		"description": "Sağlayıcılar ve ayarlar arasında hızlıca geçiş yapmak için farklı API yapılandırmalarını kaydedin.",
 		"apiProvider": "API Sağlayıcı",
 		"model": "Model",
 		"nameEmpty": "İsim boş olamaz",

+ 3 - 2
webview-ui/src/i18n/locales/vi/settings.json

@@ -3,7 +3,8 @@
 		"save": "Lưu",
 		"done": "Hoàn thành",
 		"cancel": "Hủy",
-		"reset": "Đặt lại"
+		"reset": "Đặt lại",
+		"select": "Chọn"
 	},
 	"header": {
 		"title": "Cài đặt",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "Hồ sơ cấu hình",
-		"description": "Mô tả",
+		"description": "Lưu các cấu hình API khác nhau để nhanh chóng chuyển đổi giữa các nhà cung cấp và cài đặt.",
 		"apiProvider": "Nhà cung cấp API",
 		"model": "Mẫu",
 		"nameEmpty": "Tên không được để trống",

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

@@ -3,7 +3,8 @@
 		"save": "保存",
 		"done": "完成",
 		"cancel": "取消",
-		"reset": "重置"
+		"reset": "重置",
+		"select": "选择"
 	},
 	"header": {
 		"title": "设置",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "配置文件",
-		"description": "描述",
+		"description": "保存不同的 API 配置以快速切换提供商和设置。",
 		"apiProvider": "API 提供商",
 		"model": "模型",
 		"nameEmpty": "名称不能为空",

+ 3 - 2
webview-ui/src/i18n/locales/zh-TW/settings.json

@@ -3,7 +3,8 @@
 		"save": "儲存",
 		"done": "完成",
 		"cancel": "取消",
-		"reset": "重設"
+		"reset": "重設",
+		"select": "選擇"
 	},
 	"header": {
 		"title": "設定",
@@ -74,7 +75,7 @@
 	},
 	"providers": {
 		"configProfile": "設定檔案",
-		"description": "描述",
+		"description": "儲存不同的 API 設定以快速切換提供者和設定。",
 		"apiProvider": "API 提供者",
 		"model": "模型",
 		"nameEmpty": "名稱不能為空",