From e8bc6b97941ce945d457b14a7f18d911ef838ba6 Mon Sep 17 00:00:00 2001 From: Ara Date: Thu, 29 Jan 2026 16:07:13 -0800 Subject: [PATCH] refactor: new FeatureSettingsSection UI (#8931) * feat: enable experimental features by default and update settings UI - Change ts-proto env from 'node' to 'both' for browser compatibility - Enable multiRootEnabled, enableParallelToolCalling, and skillsEnabled by default - Disable strictPlanModeEnabled by default - Add @radix-ui/react-collapsible and @radix-ui/react-slider dependencies - Remove experimental feature toggles from settings UI for cleaner interface * Fixing wording * Fixing wording * Fixing wording * Fixing wording * Fixing wording * fix: properly handle yolo mode UI when remotely locked - Use remote config value for yolo state instead of forcing false - Disable the yolo toggle when locked by remote configuration - Add visual indicator and tooltip explaining organization management * Fixing wording * Fixing wording * Fixing wording * Fixing wording --- .changeset/red-plums-cut.md | 5 + scripts/build-proto.mjs | 2 +- src/shared/storage/state-keys.ts | 8 +- webview-ui/package-lock.json | 130 +++ webview-ui/package.json | 2 + .../src/components/settings/Section.tsx | 2 +- .../components/settings/SettingsSlider.tsx | 45 + .../SubagentOutputLineLimitSlider.tsx | 38 - .../sections/FeatureSettingsSection.tsx | 779 +++++++++--------- webview-ui/src/components/ui/label.tsx | 15 + webview-ui/src/components/ui/slider.tsx | 22 + webview-ui/src/components/ui/switch.tsx | 50 +- webview-ui/src/theme.css | 2 + webview-ui/tsconfig.app.json | 7 + 14 files changed, 650 insertions(+), 457 deletions(-) create mode 100644 .changeset/red-plums-cut.md create mode 100644 webview-ui/src/components/settings/SettingsSlider.tsx delete mode 100644 webview-ui/src/components/settings/SubagentOutputLineLimitSlider.tsx create mode 100644 webview-ui/src/components/ui/label.tsx create mode 100644 webview-ui/src/components/ui/slider.tsx diff --git a/.changeset/red-plums-cut.md b/.changeset/red-plums-cut.md new file mode 100644 index 0000000000..2a6d7aec22 --- /dev/null +++ b/.changeset/red-plums-cut.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Added changes to the feature settings UI diff --git a/scripts/build-proto.mjs b/scripts/build-proto.mjs index f5c72100bd..61db5a32cd 100755 --- a/scripts/build-proto.mjs +++ b/scripts/build-proto.mjs @@ -26,7 +26,7 @@ const TS_PROTO_PLUGIN = isWindows : require.resolve("ts-proto/protoc-gen-ts_proto") const TS_PROTO_OPTIONS = [ - "env=node", + "env=both", "esModuleInterop=true", "outputServices=generic-definitions", // output generic ServiceDefinitions "outputIndex=true", // output an index file for each package which exports all protos in the package. diff --git a/src/shared/storage/state-keys.ts b/src/shared/storage/state-keys.ts index 67495c39b0..d7e48cf5cc 100644 --- a/src/shared/storage/state-keys.ts +++ b/src/shared/storage/state-keys.ts @@ -80,7 +80,7 @@ const GLOBAL_STATE_FIELDS = { mcpDisplayMode: { default: DEFAULT_MCP_DISPLAY_MODE as McpDisplayMode }, workspaceRoots: { default: undefined as WorkspaceRoot[] | undefined }, primaryRootIndex: { default: 0 as number }, - multiRootEnabled: { default: false as boolean }, + multiRootEnabled: { default: true as boolean }, lastDismissedInfoBannerVersion: { default: 0 as number }, lastDismissedModelBannerVersion: { default: 0 as number }, lastDismissedCliBannerVersion: { default: 0 as number }, @@ -252,7 +252,7 @@ const USER_SETTINGS_FIELDS = { terminalOutputLineLimit: { default: 500 as number }, maxConsecutiveMistakes: { default: 3 as number }, subagentTerminalOutputLineLimit: { default: 2000 as number }, - strictPlanModeEnabled: { default: true as boolean }, + strictPlanModeEnabled: { default: false as boolean }, yoloModeToggled: { default: false as boolean }, useAutoCondense: { default: false as boolean }, clineWebToolsEnabled: { default: true as boolean }, @@ -268,9 +268,9 @@ const USER_SETTINGS_FIELDS = { customPrompt: { default: undefined as "compact" | undefined }, autoCondenseThreshold: { default: 0.75 as number }, // number from 0 to 1 subagentsEnabled: { default: false as boolean }, - enableParallelToolCalling: { default: false as boolean }, + enableParallelToolCalling: { default: true as boolean }, backgroundEditEnabled: { default: false as boolean }, - skillsEnabled: { default: false as boolean }, + skillsEnabled: { default: true as boolean }, optOutOfRemoteConfig: { default: false as boolean }, // OpenTelemetry configuration diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index 812f8d84b8..bc7bbf6d02 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -12,12 +12,15 @@ "@fontsource/azeret-mono": "^5.2.9", "@heroui/react": "^2.8.0-beta.2", "@paper-design/shaders-react": "^0.0.46", + "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-hover-card": "^1.1.15", + "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slider": "^1.3.6", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tooltip": "^1.2.8", @@ -3800,6 +3803,36 @@ } } }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", + "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", @@ -4023,6 +4056,70 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", + "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popover": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", @@ -4253,6 +4350,39 @@ } } }, + "node_modules/@radix-ui/react-slider": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.6.tgz", + "integrity": "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", diff --git a/webview-ui/package.json b/webview-ui/package.json index 0f6101d24f..62d02ccac1 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -22,10 +22,12 @@ "@paper-design/shaders-react": "^0.0.46", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-hover-card": "^1.1.15", + "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slider": "^1.3.6", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tooltip": "^1.2.8", diff --git a/webview-ui/src/components/settings/Section.tsx b/webview-ui/src/components/settings/Section.tsx index c6afbd7b97..00d3037c77 100644 --- a/webview-ui/src/components/settings/Section.tsx +++ b/webview-ui/src/components/settings/Section.tsx @@ -3,7 +3,7 @@ import { HTMLAttributes } from "react" type SectionProps = HTMLAttributes export const Section = ({ className, ...props }: SectionProps) => ( -
+
) export default Section diff --git a/webview-ui/src/components/settings/SettingsSlider.tsx b/webview-ui/src/components/settings/SettingsSlider.tsx new file mode 100644 index 0000000000..989b2d4fb2 --- /dev/null +++ b/webview-ui/src/components/settings/SettingsSlider.tsx @@ -0,0 +1,45 @@ +import React from "react" +import { Label } from "@/components/ui/label" +import { Slider } from "@/components/ui/slider" + +interface SettingsSliderProps { + label: string + min: number + max: number + step: number + value: number + onChange: (value: number) => void + description?: string + /** Width of the value display span (default: w-12) */ + valueWidth?: string +} + +const SettingsSlider: React.FC = ({ + label, + min, + max, + step, + value, + onChange, + description, + valueWidth = "w-12", +}) => { + const handleSliderChange = (values: number[]) => { + onChange(values[0]) + } + + return ( +
+
+
+ +
+ {value} +
+ + {description &&

{description}

} +
+ ) +} + +export default SettingsSlider diff --git a/webview-ui/src/components/settings/SubagentOutputLineLimitSlider.tsx b/webview-ui/src/components/settings/SubagentOutputLineLimitSlider.tsx deleted file mode 100644 index 487579b7c2..0000000000 --- a/webview-ui/src/components/settings/SubagentOutputLineLimitSlider.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from "react" -import { useExtensionState } from "@/context/ExtensionStateContext" -import { updateSetting } from "./utils/settingsHandlers" - -const SubagentOutputLineLimitSlider: React.FC = () => { - const { subagentTerminalOutputLineLimit } = useExtensionState() - - const handleSliderChange = (event: React.ChangeEvent) => { - const value = parseInt(event.target.value, 10) - updateSetting("subagentTerminalOutputLineLimit", value) - } - - return ( -
- -
- - {subagentTerminalOutputLineLimit ?? 2000} -
-

- Maximum number of lines to include in output from CLI subagents. Truncates middle to save tokens. -

-
- ) -} - -export default SubagentOutputLineLimitSlider diff --git a/webview-ui/src/components/settings/sections/FeatureSettingsSection.tsx b/webview-ui/src/components/settings/sections/FeatureSettingsSection.tsx index fafd242f6d..6721df6a3b 100644 --- a/webview-ui/src/components/settings/sections/FeatureSettingsSection.tsx +++ b/webview-ui/src/components/settings/sections/FeatureSettingsSection.tsx @@ -1,18 +1,186 @@ -import { McpDisplayMode } from "@shared/McpDisplayMode" +import { UpdateSettingsRequest } from "@shared/proto/cline/state" import { EmptyRequest } from "@shared/proto/index.cline" import { OpenaiReasoningEffort } from "@shared/storage/types" -import { VSCodeButton, VSCodeCheckbox, VSCodeDropdown, VSCodeOption, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { memo, useEffect, useState } from "react" -import McpDisplayModeDropdown from "@/components/mcp/chat-display/McpDisplayModeDropdown" +import { AlertCircleIcon } from "lucide-react" +import { memo, type ReactNode, useCallback, useEffect, useState } from "react" +import { Button } from "@/components/ui/button" +import { Label } from "@/components/ui/label" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Switch } from "@/components/ui/switch" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" import { PLATFORM_CONFIG, PlatformType } from "@/config/platform.config" import { useExtensionState } from "@/context/ExtensionStateContext" import { StateServiceClient } from "@/services/grpc-client" import { isMacOSOrLinux } from "@/utils/platformUtils" import Section from "../Section" -import SubagentOutputLineLimitSlider from "../SubagentOutputLineLimitSlider" +import SettingsSlider from "../SettingsSlider" import { updateSetting } from "../utils/settingsHandlers" +// Reusable checkbox component for feature settings +interface FeatureCheckboxProps { + checked: boolean | undefined + onChange: (checked: boolean) => void + label: string + description: ReactNode + disabled?: boolean + isExperimental?: boolean + isRemoteLocked?: boolean + remoteTooltip?: string + isVisible?: boolean +} + +// Interface for feature toggle configuration +interface FeatureToggle { + id: string + label: string + description: ReactNode + settingKey: keyof UpdateSettingsRequest + stateKey: string + isExperimental?: boolean + /** If set, the setting value is nested with this key (e.g., "enabled" -> { enabled: checked }) */ + nestedKey?: string +} + +const agentFeatures: FeatureToggle[] = [ + { + id: "native-tool-call", + label: "Native Tool Call", + description: "Use native function calling when available", + stateKey: "nativeToolCallSetting", + settingKey: "nativeToolCallEnabled", + }, + { + id: "parallel-tool-calling", + label: "Parallel Tool Calling", + description: "Execute multiple tool calls simultaneously", + stateKey: "enableParallelToolCalling", + settingKey: "enableParallelToolCalling", + isExperimental: true, + }, + { + id: "strict-plan-mode", + label: "Strict Plan Mode", + description: "Prevents file edits while in Plan mode", + stateKey: "strictPlanModeEnabled", + settingKey: "strictPlanModeEnabled", + }, + { + id: "auto-compact", + label: "Auto Compact", + description: "Automatically compress conversation history.", + stateKey: "useAutoCondense", + settingKey: "useAutoCondense", + }, +] + +const editorFeatures: FeatureToggle[] = [ + { + id: "background-edit", + label: "Background Edit", + description: "Allow edits without stealing editor focus", + stateKey: "backgroundEditEnabled", + settingKey: "backgroundEditEnabled", + isExperimental: true, + }, + { + id: "checkpoints", + label: "Checkpoints", + description: "Save progress at key points for easy rollback", + stateKey: "enableCheckpointsSetting", + settingKey: "enableCheckpointsSetting", + }, + { + id: "cline-web-tools", + label: "Cline Web Tools", + description: "Access web browsing and search capabilities", + stateKey: "clineWebToolsEnabled", + settingKey: "clineWebToolsEnabled", + }, + { + id: "worktrees", + label: "Worktrees", + description: "Enables git worktree management for running parallel Cline tasks.", + stateKey: "worktreesEnabled", + settingKey: "worktreesEnabled", + }, +] + +const experimentalFeatures: FeatureToggle[] = [ + { + id: "yolo", + label: "Yolo Mode", + description: + "Execute tasks without user's confirmation. Auto-switches from Plan to Act mode and disables the ask question tool. Use with extreme caution.", + stateKey: "yoloModeToggled", + settingKey: "yoloModeToggled", + isExperimental: true, + }, + { + id: "focus-chain", + label: "Focus Chain", + description: "Maintain context focus across interactions", + stateKey: "focusChainEnabled", + settingKey: "focusChainSettings", + nestedKey: "enabled", + }, +] + +const FeatureRow = memo( + ({ + checked = false, + onChange, + label, + description, + disabled, + isExperimental, + isRemoteLocked, + isVisible = true, + remoteTooltip, + }: FeatureCheckboxProps) => { + if (!isVisible) { + return null + } + + const checkbox = ( +
+
{label}
+
+ + {isRemoteLocked && } +
+
+ ) + + return ( +
+
+ {isRemoteLocked ? ( + + {checkbox} + + {remoteTooltip} + + + ) : ( + checkbox + )} +
+
+ {isExperimental && Experimental: } + {description} +
+
+ ) + }, +) + interface FeatureSettingsSectionProps { renderSectionHeader: (tabId: string) => JSX.Element | null } @@ -24,15 +192,13 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP openaiReasoningEffort, strictPlanModeEnabled, yoloModeToggled, - dictationSettings, useAutoCondense, clineWebToolsEnabled, worktreesEnabled, focusChainSettings, - multiRootSetting, - skillsEnabled, remoteConfigSettings, subagentsEnabled, + subagentTerminalOutputLineLimit, nativeToolCallSetting, enableParallelToolCalling, backgroundEditEnabled, @@ -40,9 +206,9 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP const [isClineCliInstalled, setIsClineCliInstalled] = useState(false) - const handleReasoningEffortChange = (newValue: OpenaiReasoningEffort) => { + const handleReasoningEffortChange = useCallback((newValue: OpenaiReasoningEffort) => { updateSetting("openaiReasoningEffort", newValue) - } + }, []) // Poll for CLI installation status while the component is mounted useEffect(() => { @@ -56,411 +222,230 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP } checkInstallation() - - // Poll ever 1.5 seconds to see if CLI is installed (only when form is open) const pollInterval = setInterval(checkInstallation, 1500) + return () => clearInterval(pollInterval) + }, []) - return () => { - clearInterval(pollInterval) + const handleInstallCli = useCallback(async () => { + try { + await StateServiceClient.installClineCli(EmptyRequest.create()) + } catch (error) { + console.error("Failed to initiate CLI installation:", error) } }, []) + const handleFocusChainIntervalChange = useCallback( + (value: number) => { + updateSetting("focusChainSettings", { ...focusChainSettings, remindClineInterval: value }) + }, + [focusChainSettings], + ) + + const showSubagents = isMacOSOrLinux() && PLATFORM_CONFIG.type === PlatformType.VSCODE + const isYoloRemoteLocked = remoteConfigSettings?.yoloModeToggled !== undefined + + // State lookup for mapped features + const featureState: Record = { + enableCheckpointsSetting, + strictPlanModeEnabled, + nativeToolCallSetting, + focusChainEnabled: focusChainSettings?.enabled, + useAutoCondense, + clineWebToolsEnabled: clineWebToolsEnabled?.user, + worktreesEnabled: worktreesEnabled?.user, + enableParallelToolCalling, + backgroundEditEnabled, + yoloModeToggled: isYoloRemoteLocked ? remoteConfigSettings?.yoloModeToggled : yoloModeToggled, + } + + // Visibility lookup for features with feature flags + const featureVisibility: Record = { + clineWebToolsEnabled: clineWebToolsEnabled?.featureFlag, + worktreesEnabled: worktreesEnabled?.featureFlag, + } + + // Handler for feature toggle changes, supports nested settings like focusChainSettings + const handleFeatureChange = useCallback( + (feature: FeatureToggle, checked: boolean) => { + if (feature.nestedKey) { + // For nested settings, spread the existing value and set the nested key + let currentValue = {} + if (feature.settingKey === "focusChainSettings") { + currentValue = focusChainSettings ?? {} + } + updateSetting(feature.settingKey, { ...currentValue, [feature.nestedKey]: checked }) + } else { + updateSetting(feature.settingKey, checked) + } + }, + [focusChainSettings], + ) + return ( -
+
{renderSectionHeader("features")}
-
- {/* Subagents - Only show on macOS and Linux */} - {isMacOSOrLinux() && PLATFORM_CONFIG.type === PlatformType.VSCODE && ( +
+ {/* Core features */} +
+
Agent
-
- NEW -
+ className="relative p-3 pt-0 my-3 rounded-md border border-editor-widget-border/50" + id="agent-features"> + {agentFeatures.map((feature) => ( + updateSetting(feature.settingKey, checked)} + /> + ))} +
+
-
-

- - - Cline for CLI is required for subagents. Install it with: - - npm install -g cline - - , then run - - cline auth - - To authenticate with Cline or configure an API provider. - -

- {!isClineCliInstalled && ( - { - try { - await StateServiceClient.installClineCli(EmptyRequest.create()) - } catch (error) { - console.error("Failed to initiate CLI installation:", error) - } - }} - style={{ - transform: "scale(0.85)", - transformOrigin: "left center", - marginLeft: "-2px", - }}> - Install Now - - )} -
- { - const checked = e.target.checked === true - updateSetting("subagentsEnabled", checked) - }}> - - {subagentsEnabled ? "Subagents Enabled" : "Enable Subagents"} - - -

- Experimental: {" "} - - Allows Cline to spawn subprocesses to handle focused tasks like exploring large codebases, - keeping your main context clean. - -

- {subagentsEnabled && ( -
- -
+ {/* Optional features */} +
+
Editor
+
+ {editorFeatures.map((feature) => ( + handleFeatureChange(feature, checked)} + /> + ))} +
+
+ + {/* Experimental features */} +
+
Experimental
+
+ {/* Subagents - Only show on macOS and Linux */} + {showSubagents && ( + <> + updateSetting("subagentsEnabled", checked)} + /> +
+

+ + + Cline CLI is required for subagents. Install it with + npm install -g cline, then run + cline auth + to authenticate with Cline or configure an API provider. + +

+ {!isClineCliInstalled && ( + + )} +
+ {subagentsEnabled && ( + updateSetting("subagentTerminalOutputLineLimit", value)} + step={100} + value={subagentTerminalOutputLineLimit ?? 2000} + /> + )} + + )} + {experimentalFeatures.map((feature) => ( + handleFeatureChange(feature, checked)} + remoteTooltip="This setting is managed by your organization's remote configuration" + /> + ))} + {focusChainSettings?.enabled && ( + )}
- )} +
+
-
- { - const checked = e.target.checked === true - updateSetting("enableCheckpointsSetting", checked) - }}> - Enable Checkpoints - -

- Enables extension to save checkpoints of workspace throughout the task. Uses git under the hood which - may not work well with large workspaces. -

-
-
- - updateSetting("mcpDisplayMode", newMode)} - value={mcpDisplayMode} - /> -

- Controls how MCP responses are displayed: plain text, rich formatting with links/images, or markdown - rendering. -

-
-
- - { - const newValue = e.target.currentValue as OpenaiReasoningEffort - handleReasoningEffortChange(newValue) - }}> - Minimal - Low - Medium - High - -

- Reasoning effort for the OpenAI family of models(applies to all OpenAI model providers) -

-
-
- { - const checked = e.target.checked === true - updateSetting("strictPlanModeEnabled", checked) - }}> - Enable strict plan mode - -

- Enforces strict tool use while in plan mode, preventing file edits. -

-
- { -
- { - const checked = e.target.checked === true - updateSetting("focusChainSettings", { ...focusChainSettings, enabled: checked }) - }}> - Enable Focus Chain - -

- Enables enhanced task progress tracking and automatic focus chain list management throughout - tasks. -

-
- } - {focusChainSettings?.enabled && ( -
- - { - const value = parseInt(e.target.value, 10) - if (!Number.isNaN(value) && value >= 1 && value <= 100) { - updateSetting("focusChainSettings", { - ...focusChainSettings, - remindClineInterval: value, - }) - } - }} - value={String(focusChainSettings?.remindClineInterval || 6)} - /> -

- Interval (in messages) to remind Cline about its focus chain checklist (1-100). Lower values - provide more frequent reminders. -

-
- )} - {dictationSettings?.featureEnabled && ( -
- { - const checked = e.target.checked === true - const updatedDictationSettings = { - ...dictationSettings, - dictationEnabled: checked, - } - updateSetting("dictationSettings", updatedDictationSettings) - }}> - Enable Dictation - -

- Enables speech-to-text transcription using your Cline account. Uses the Aqua Voice's Avalon model, - at $0.0065 credits per minute of audio processed. 5 minutes max per message. -

-
- )} -
- { - const checked = e.target.checked === true - updateSetting("useAutoCondense", checked) - }}> - Enable Auto Compact - -

- Enables advanced context management system which uses LLM based condensing for next-gen models.{" "} - - Learn more - -

-
- {clineWebToolsEnabled?.featureFlag && ( -
- { - const checked = e.target.checked === true - updateSetting("clineWebToolsEnabled", checked) - }}> - Enable Cline Web Tools - -

- Enables websearch and webfetch tools while using the Cline provider. -

-
- )} - {worktreesEnabled?.featureFlag && ( -
- { - const checked = e.target.checked === true - updateSetting("worktreesEnabled", checked) - }}> - Enable Worktrees - -

- Enables git worktree management for running parallel Cline tasks. -

-
- )} -
- { - const enabled = (e?.target as HTMLInputElement).checked - updateSetting("nativeToolCallEnabled", enabled) - }}> - Enable Native Tool Call - -

- Uses the model's native tool calling API instead of XML-based tool parsing. This will improve - performance for supported models. -

-
-
- { - const enabled = (e?.target as HTMLInputElement).checked - updateSetting("enableParallelToolCalling", enabled) - }}> - Enable Parallel Tool Calling - -

- Experimental: {" "} - - Allows models to call multiple tools in a single response. Automatically enabled for GPT-5 models. - -

-
-
- { - const checked = e.target.checked === true - updateSetting("backgroundEditEnabled", checked) - }}> - Enable Background Edit - -

- Experimental: - - Allows editing files in background without opening the diff view in editor. - -

-
- {multiRootSetting.featureFlag && ( -
- { - const checked = e.target.checked === true - updateSetting("multiRootEnabled", checked) - }}> - Enable Multi-Root Workspace - -

- Experimental: {" "} - Allows cline to work across multiple workspaces. -

-
- )} -
- { - const checked = e.target.checked === true - updateSetting("skillsEnabled", checked) - }}> - Enable Skills - -

- Experimental: {" "} - - Enables Skills for reusable, on-demand agent instructions from .cline/skills/ directories. - -

-
-
- - -
- { - const checked = e.target.checked === true - updateSetting("yoloModeToggled", checked) - }}> - Enable YOLO Mode - - {remoteConfigSettings?.yoloModeToggled !== undefined && ( - - )} -
-
- -
+ {/* Advanced */} +
+
Advanced
+
+
+ {/* OAI Reasoning Effort */} +
+ +

+ Control the depth of reasoning for OpenAI o-series models +

+ +
-

- EXPERIMENTAL & DANGEROUS: This mode disables safety checks and user confirmations. Cline will - automatically approve all actions without asking. Use with extreme caution. -

+ {/* MCP Display Mode */} +
+ +

Controls how MCP responses are displayed

+ +
+
) } - export default memo(FeatureSettingsSection) diff --git a/webview-ui/src/components/ui/label.tsx b/webview-ui/src/components/ui/label.tsx new file mode 100644 index 0000000000..0bfe7623f3 --- /dev/null +++ b/webview-ui/src/components/ui/label.tsx @@ -0,0 +1,15 @@ +import * as LabelPrimitive from "@radix-ui/react-label" +import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react" + +import { cn } from "@/lib/utils" + +const labelVariants = cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70") + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & VariantProps +>(({ className, ...props }, ref) => ) +Label.displayName = LabelPrimitive.Root.displayName + +export { Label } diff --git a/webview-ui/src/components/ui/slider.tsx b/webview-ui/src/components/ui/slider.tsx new file mode 100644 index 0000000000..42d2294982 --- /dev/null +++ b/webview-ui/src/components/ui/slider.tsx @@ -0,0 +1,22 @@ +import * as SliderPrimitive from "@radix-ui/react-slider" +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Slider = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + + +)) +Slider.displayName = SliderPrimitive.Root.displayName + +export { Slider } diff --git a/webview-ui/src/components/ui/switch.tsx b/webview-ui/src/components/ui/switch.tsx index 93a98a2abf..b0a1ac2077 100644 --- a/webview-ui/src/components/ui/switch.tsx +++ b/webview-ui/src/components/ui/switch.tsx @@ -3,24 +3,42 @@ import * as React from "react" import { cn } from "@/lib/utils" -const Switch = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - = { + default: { + root: "h-3 w-6", + thumb: "h-2 w-2 data-[state=checked]:translate-x-2.5", + }, + lg: { + root: "h-5 w-10", + thumb: "h-4 w-4 data-[state=checked]:translate-x-[1.3rem]", + }, +} + +interface SwitchProps extends React.ComponentPropsWithoutRef { + size?: SwitchSize +} + +const Switch = React.forwardRef, SwitchProps>( + ({ className, size = "default", ...props }, ref) => ( + - -)) + {...props} + ref={ref}> + + + ), +) Switch.displayName = SwitchPrimitives.Root.displayName export { Switch } diff --git a/webview-ui/src/theme.css b/webview-ui/src/theme.css index dad113f073..88f4ab8bc9 100644 --- a/webview-ui/src/theme.css +++ b/webview-ui/src/theme.css @@ -29,6 +29,8 @@ --color-input-placeholder: var(--vscode-input-placeholderForeground); --color-input-error-background: var(--vscode-inputValidation-errorBackground, #a82424); --color-input-error-foreground: var(--vscode-inputValidation-errorForeground, #8f1f1f); + --color-input-warning-background: var(--vscode-inputValidation-warningBackground, #663c00); + --color-input-warning-foreground: var(--vscode-inputValidation-warningForeground, #7e5100); --color-selection: var(--vscode-list-activeSelectionBackground); --color-selection-foreground: var(--vscode-list-activeSelectionForeground); --color-selection-inactive: var(--vscode-editor-inactiveSelectionBackground); diff --git a/webview-ui/tsconfig.app.json b/webview-ui/tsconfig.app.json index 86a6563770..58d3cc7f97 100644 --- a/webview-ui/tsconfig.app.json +++ b/webview-ui/tsconfig.app.json @@ -50,5 +50,12 @@ }, "include": [ "src" + ], + "exclude": [ + "src/**/*.test.tsx", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.spec.ts", + "src/**/__tests__/**" ] }