Compare commits

...

7 Commits

Author SHA1 Message Date
celestial-vault e67bb6c636 merge conflicts 2025-06-27 15:36:36 -07:00
celestial-vault fa7794e9a6 move files to sections folder 2025-06-27 15:00:06 -07:00
celestial-vault 88029834be move terminal, browser, and feature settings 2025-06-27 14:55:06 -07:00
celestial-vault 1f267d058a duplicate import 2025-06-27 14:26:35 -07:00
celestial-vault 853b8a6470 merge conflicts 2025-06-27 14:23:04 -07:00
celestial-vault a5258e46e1 add general settings section 2025-06-27 10:24:15 -07:00
celestial-vault 36dfc7de11 refactor out apiconfig section 2025-06-27 10:04:57 -07:00
7 changed files with 455 additions and 422 deletions
@@ -1,104 +0,0 @@
import { VSCodeCheckbox, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { memo } from "react"
import { OpenAIReasoningEffort } from "@shared/ChatSettings"
const FeatureSettingsSection = () => {
const {
enableCheckpointsSetting,
setEnableCheckpointsSetting,
mcpMarketplaceEnabled,
setMcpMarketplaceEnabled,
mcpRichDisplayEnabled,
setMcpRichDisplayEnabled,
mcpResponsesCollapsed,
setMcpResponsesCollapsed,
chatSettings,
setChatSettings,
} = useExtensionState()
return (
<div style={{ marginBottom: 20 }}>
<div>
<VSCodeCheckbox
checked={enableCheckpointsSetting}
onChange={(e: any) => {
const checked = e.target.checked === true
setEnableCheckpointsSetting(checked)
}}>
Enable Checkpoints
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
Enables extension to save checkpoints of workspace throughout the task. Uses git under the hood which may not
work well with large workspaces.
</p>
</div>
<div style={{ marginTop: 10 }}>
<VSCodeCheckbox
checked={mcpMarketplaceEnabled}
onChange={(e: any) => {
const checked = e.target.checked === true
setMcpMarketplaceEnabled(checked)
}}>
Enable MCP Marketplace
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
Enables the MCP Marketplace tab for discovering and installing MCP servers.
</p>
</div>
<div style={{ marginTop: 10 }}>
<VSCodeCheckbox
checked={mcpRichDisplayEnabled}
onChange={(e: any) => {
const checked = e.target.checked === true
setMcpRichDisplayEnabled(checked)
}}>
Enable Rich MCP Display
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
Enables rich formatting for MCP responses. When disabled, responses will be shown in plain text.
</p>
</div>
<div style={{ marginTop: 10 }}>
<VSCodeCheckbox
checked={mcpResponsesCollapsed}
onChange={(e: any) => {
const checked = e.target.checked === true
setMcpResponsesCollapsed(checked)
}}>
Collapse MCP Responses
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
Sets the default display mode for MCP response panels
</p>
</div>
<div style={{ marginTop: 10 }}>
<label
htmlFor="openai-reasoning-effort-dropdown"
className="block text-sm font-medium text-[var(--vscode-foreground)] mb-1">
OpenAI Reasoning Effort
</label>
<VSCodeDropdown
id="openai-reasoning-effort-dropdown"
currentValue={chatSettings.openAIReasoningEffort || "medium"}
onChange={(e: any) => {
const newValue = e.target.currentValue as OpenAIReasoningEffort
setChatSettings({
...chatSettings,
openAIReasoningEffort: newValue,
})
}}
className="w-full">
<VSCodeOption value="low">Low</VSCodeOption>
<VSCodeOption value="medium">Medium</VSCodeOption>
<VSCodeOption value="high">High</VSCodeOption>
</VSCodeDropdown>
<p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
Reasoning effort for the OpenAI family of models(applies to all OpenAI model providers)
</p>
</div>
</div>
)
}
export default memo(FeatureSettingsSection)
@@ -11,16 +11,17 @@ import { CheckCheck, FlaskConical, Info, LucideIcon, Settings, SquareMousePointe
import { memo, useCallback, useEffect, useRef, useState } from "react"
import { useEvent } from "react-use"
import { Tab, TabContent, TabHeader, TabList, TabTrigger } from "../common/Tab"
import BrowserSettingsSection from "./BrowserSettingsSection"
import { BrowserSettings } from "@shared/BrowserSettings"
import FeatureSettingsSection from "./FeatureSettingsSection"
import FeatureSettingsSection from "./sections/FeatureSettingsSection"
import Section from "./Section"
import SectionHeader from "./SectionHeader"
import TerminalSettingsSection from "./TerminalSettingsSection"
import TerminalSettingsSection from "./sections/TerminalSettingsSection"
import ApiConfigurationSection from "./sections/ApiConfigurationSection"
import GeneralSettingsSection from "./sections/GeneralSettingsSection"
import BrowserSettingsSection from "./sections/BrowserSettingsSection"
import { convertApiConfigurationToProtoApiConfiguration } from "@shared/proto-conversions/state/settings-conversion"
import { convertChatSettingsToProtoChatSettings } from "@shared/proto-conversions/state/chat-settings-conversion"
const IS_DEV = process.env.IS_DEV
// Styles for the tab system
@@ -783,37 +784,19 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
)}
{/* Feature Settings Tab */}
{activeTab === "features" && (
<div>
{renderSectionHeader("features")}
<Section>
<FeatureSettingsSection />
</Section>
</div>
)}
{activeTab === "features" && <FeatureSettingsSection renderSectionHeader={renderSectionHeader} />}
{/* Browser Settings Tab */}
{activeTab === "browser" && (
<div>
{renderSectionHeader("browser")}
<Section>
<BrowserSettingsSection
localBrowserSettings={localBrowserSettings}
onBrowserSettingsChange={setLocalBrowserSettings}
/>
</Section>
</div>
<BrowserSettingsSection
localBrowserSettings={localBrowserSettings}
onBrowserSettingsChange={setLocalBrowserSettings}
renderSectionHeader={renderSectionHeader}
/>
)}
{/* Terminal Settings Tab */}
{activeTab === "terminal" && (
<div>
{renderSectionHeader("terminal")}
<Section>
<TerminalSettingsSection />
</Section>
</div>
)}
{activeTab === "terminal" && <TerminalSettingsSection renderSectionHeader={renderSectionHeader} />}
{/* Debug Tab (only in dev mode) */}
{IS_DEV && activeTab === "debug" && (
@@ -1,142 +0,0 @@
import React, { useState, useEffect } from "react"
import { VSCodeTextField, VSCodeCheckbox, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import TerminalOutputLineLimitSlider from "./TerminalOutputLineLimitSlider"
import { StateServiceClient } from "../../services/grpc-client"
import { Int64, Int64Request } from "@shared/proto/common"
export const TerminalSettingsSection: React.FC = () => {
const {
shellIntegrationTimeout,
setShellIntegrationTimeout,
terminalReuseEnabled,
setTerminalReuseEnabled,
defaultTerminalProfile,
setDefaultTerminalProfile,
availableTerminalProfiles,
platform,
} = useExtensionState()
const [inputValue, setInputValue] = useState((shellIntegrationTimeout / 1000).toString())
const [inputError, setInputError] = useState<string | null>(null)
const handleTimeoutChange = (event: Event) => {
const target = event.target as HTMLInputElement
const value = target.value
setInputValue(value)
const seconds = parseFloat(value)
if (isNaN(seconds) || seconds <= 0) {
setInputError("Please enter a positive number")
return
}
setInputError(null)
const timeout = Math.round(seconds * 1000)
setShellIntegrationTimeout(timeout)
StateServiceClient.updateTerminalConnectionTimeout({
value: timeout,
} as Int64Request)
.then((response: Int64) => {
setShellIntegrationTimeout(response.value)
setInputValue((response.value / 1000).toString())
})
.catch((error) => {
console.error("Failed to update terminal connection timeout:", error)
})
}
const handleInputBlur = () => {
if (inputError) {
setInputValue((shellIntegrationTimeout / 1000).toString())
setInputError(null)
}
}
const handleTerminalReuseChange = (event: Event) => {
const target = event.target as HTMLInputElement
const checked = target.checked
setTerminalReuseEnabled(checked)
StateServiceClient.updateTerminalReuseEnabled({ value: checked } as any).catch((error) => {
console.error("Failed to update terminal reuse enabled:", error)
})
}
// Use any to avoid type conflicts between Event and FormEvent
const handleDefaultTerminalProfileChange = (event: any) => {
const target = event.target as HTMLSelectElement
const profileId = target.value
// Only update the local state, let the Save button handle the backend update
setDefaultTerminalProfile(profileId)
}
const profilesToShow = availableTerminalProfiles
return (
<div id="terminal-settings-section" style={{ marginBottom: 20 }}>
<div style={{ marginBottom: 15 }}>
<label htmlFor="default-terminal-profile" style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>
Default Terminal Profile
</label>
<VSCodeDropdown
id="default-terminal-profile"
value={defaultTerminalProfile || "default"}
onChange={handleDefaultTerminalProfileChange}
style={{ width: "100%" }}>
{profilesToShow.map((profile) => (
<VSCodeOption key={profile.id} value={profile.id} title={profile.description}>
{profile.name}
</VSCodeOption>
))}
</VSCodeDropdown>
<p style={{ fontSize: "12px", color: "var(--vscode-descriptionForeground)", margin: "5px 0 0 0" }}>
Select the default terminal Cline will use. 'Default' uses your VSCode global setting.
</p>
</div>
<div style={{ marginBottom: 15 }}>
<div style={{ marginBottom: 8 }}>
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>
Shell integration timeout (seconds)
</label>
<div style={{ display: "flex", alignItems: "center" }}>
<VSCodeTextField
style={{ width: "100%" }}
value={inputValue}
placeholder="Enter timeout in seconds"
onChange={(event) => handleTimeoutChange(event as Event)}
onBlur={handleInputBlur}
/>
</div>
{inputError && (
<div style={{ color: "var(--vscode-errorForeground)", fontSize: "12px", marginTop: 5 }}>{inputError}</div>
)}
</div>
<p style={{ fontSize: "12px", color: "var(--vscode-descriptionForeground)", margin: 0 }}>
Set how long Cline waits for shell integration to activate before executing commands. Increase this value if
you experience terminal connection timeouts.
</p>
</div>
<div style={{ marginBottom: 15 }}>
<div style={{ display: "flex", alignItems: "center", marginBottom: 8 }}>
<VSCodeCheckbox
checked={terminalReuseEnabled ?? true}
onChange={(event) => handleTerminalReuseChange(event as Event)}>
Enable aggressive terminal reuse
</VSCodeCheckbox>
</div>
<p style={{ fontSize: "12px", color: "var(--vscode-descriptionForeground)", margin: 0 }}>
When enabled, Cline will reuse existing terminal windows that aren't in the current working directory. Disable
this if you experience issues with task lockout after a terminal command.
</p>
</div>
<TerminalOutputLineLimitSlider />
</div>
)
}
export default TerminalSettingsSection
@@ -2,15 +2,17 @@ import { VSCodeButton, VSCodeCheckbox, VSCodeDropdown, VSCodeOption, VSCodeTextF
import debounce from "debounce"
import React, { useCallback, useEffect, useState } from "react"
import styled from "styled-components"
import { BROWSER_VIEWPORT_PRESETS } from "../../../../src/shared/BrowserSettings"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { BrowserServiceClient } from "../../services/grpc-client"
import { BROWSER_VIEWPORT_PRESETS } from "../../../../../src/shared/BrowserSettings"
import { useExtensionState } from "../../../context/ExtensionStateContext"
import { BrowserServiceClient } from "../../../services/grpc-client"
import { EmptyRequest, StringRequest } from "@shared/proto/common"
import { BrowserSettings } from "@shared/BrowserSettings"
import Section from "../Section"
interface BrowserSettingsSectionProps {
localBrowserSettings: BrowserSettings
onBrowserSettingsChange: (settings: BrowserSettings) => void
renderSectionHeader: (tabId: string) => JSX.Element | null
}
const ConnectionStatusIndicator = ({
@@ -59,6 +61,7 @@ const CollapsibleContent = styled.div<{ isOpen: boolean }>`
export const BrowserSettingsSection: React.FC<BrowserSettingsSectionProps> = ({
localBrowserSettings,
onBrowserSettingsChange,
renderSectionHeader,
}) => {
const { browserSettings } = useExtensionState()
const [localChromePath, setLocalChromePath] = useState(localBrowserSettings.chromeExecutablePath || "")
@@ -275,161 +278,184 @@ export const BrowserSettingsSection: React.FC<BrowserSettingsSectionProps> = ({
const isSubSettingsOpen = !(localBrowserSettings.disableToolUse || false)
return (
<div id="browser-settings-section" style={{ marginBottom: 20 }}>
{/* Master Toggle */}
<div style={{ marginBottom: isSubSettingsOpen ? 0 : 10 }}>
<VSCodeCheckbox
checked={localBrowserSettings.disableToolUse || false}
onChange={(e) => updateDisableToolUse((e.target as HTMLInputElement).checked)}>
Disable browser tool usage
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
margin: "4px 0 0 0px",
}}>
Prevent Cline from using browser actions (e.g. launch, click, type).
</p>
</div>
<CollapsibleContent isOpen={isSubSettingsOpen}>
<div style={{ marginBottom: 15 }}>
<div style={{ marginBottom: 8 }}>
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Viewport size</label>
<VSCodeDropdown
style={{ width: "100%" }}
value={
Object.entries(BROWSER_VIEWPORT_PRESETS).find(([_, size]) => {
const typedSize = size as { width: number; height: number }
return (
typedSize.width === localBrowserSettings.viewport.width &&
typedSize.height === localBrowserSettings.viewport.height
)
})?.[0]
}
onChange={(event) => handleViewportChange(event as Event)}>
{Object.entries(BROWSER_VIEWPORT_PRESETS).map(([name]) => (
<VSCodeOption key={name} value={name}>
{name}
</VSCodeOption>
))}
</VSCodeDropdown>
</div>
<p
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
margin: 0,
}}>
Set the size of the browser viewport for screenshots and interactions.
</p>
</div>
<div style={{ marginBottom: 0 }}>
{" "}
{/* This div now contains Remote Connection & Chrome Path */}
<div style={{ marginBottom: 4, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<div>
{renderSectionHeader("browser")}
<Section>
<div id="browser-settings-section" style={{ marginBottom: 20 }}>
{/* Master Toggle */}
<div style={{ marginBottom: isSubSettingsOpen ? 0 : 10 }}>
<VSCodeCheckbox
checked={localBrowserSettings.remoteBrowserEnabled}
onChange={(e) => updateRemoteBrowserEnabled((e.target as HTMLInputElement).checked)}>
Use remote browser connection
checked={localBrowserSettings.disableToolUse || false}
onChange={(e) => updateDisableToolUse((e.target as HTMLInputElement).checked)}>
Disable browser tool usage
</VSCodeCheckbox>
<ConnectionStatusIndicator
isChecking={isCheckingConnection}
isConnected={connectionStatus}
remoteBrowserEnabled={localBrowserSettings.remoteBrowserEnabled}
/>
<p
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
margin: "4px 0 0 0px",
}}>
Prevent Cline from using browser actions (e.g. launch, click, type).
</p>
</div>
<p
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
margin: "0 0 6px 0px",
}}>
Enable Cline to use your Chrome
{isBundled ? "(not detected on your machine)" : detectedChromePath ? ` (${detectedChromePath})` : ""}. You
can specify a custom path below. Using a remote browser connection requires starting Chrome in debug mode
{localBrowserSettings.remoteBrowserEnabled ? (
<>
{" "}
manually (<code>--remote-debugging-port=9222</code>) or using the button below. Enter the host
address or leave it blank for automatic discovery.
</>
) : (
"."
)}
</p>
{/* Moved remote-specific settings to appear directly after enabling remote connection */}
{localBrowserSettings.remoteBrowserEnabled && (
<div style={{ marginLeft: 0, marginTop: 8 }}>
<VSCodeTextField
value={localBrowserSettings.remoteBrowserHost || ""}
placeholder="http://localhost:9222"
style={{ width: "100%", marginBottom: 8 }}
onChange={(e: any) => updateRemoteBrowserHost(e.target.value || undefined)}
/>
{shouldShowRelaunchButton && (
<div style={{ display: "flex", gap: "10px", marginBottom: 8, justifyContent: "center" }}>
<VSCodeButton style={{ flex: 1 }} disabled={debugMode} onClick={relaunchChromeDebugMode}>
{debugMode ? "Launching Browser..." : "Launch Browser with Debug Mode"}
</VSCodeButton>
</div>
)}
{relaunchResult && (
<div
style={{
padding: "8px",
marginBottom: "8px",
backgroundColor: relaunchResult.success ? "rgba(0, 128, 0, 0.1)" : "rgba(255, 0, 0, 0.1)",
color: relaunchResult.success
? "var(--vscode-terminal-ansiGreen)"
: "var(--vscode-terminal-ansiRed)",
borderRadius: "3px",
fontSize: "11px",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
}}>
{relaunchResult.message}
</div>
)}
<CollapsibleContent isOpen={isSubSettingsOpen}>
<div style={{ marginBottom: 15 }}>
<div style={{ marginBottom: 8 }}>
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Viewport size</label>
<VSCodeDropdown
style={{ width: "100%" }}
value={
Object.entries(BROWSER_VIEWPORT_PRESETS).find(([_, size]) => {
const typedSize = size as { width: number; height: number }
return (
typedSize.width === localBrowserSettings.viewport.width &&
typedSize.height === localBrowserSettings.viewport.height
)
})?.[0]
}
onChange={(event) => handleViewportChange(event as Event)}>
{Object.entries(BROWSER_VIEWPORT_PRESETS).map(([name]) => (
<VSCodeOption key={name} value={name}>
{name}
</VSCodeOption>
))}
</VSCodeDropdown>
</div>
<p
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
margin: 0,
}}></p>
}}>
Set the size of the browser viewport for screenshots and interactions.
</p>
</div>
)}
{/* Chrome Executable Path section now follows remote-specific settings */}
<div style={{ marginBottom: 8, marginTop: 8 }}>
<label htmlFor="chrome-executable-path" style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>
Chrome Executable Path (Optional)
</label>
<VSCodeTextField
id="chrome-executable-path"
value={localChromePath}
placeholder="e.g., /usr/bin/google-chrome or C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
style={{ width: "100%" }}
onChange={(e: any) => {
const newValue = e.target.value || ""
updateChromeExecutablePath(newValue)
}}
/>
<p
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
margin: "4px 0 0 0",
}}>
Leave blank to auto-detect.
</p>
</div>
<div style={{ marginBottom: 0 }}>
{" "}
{/* This div now contains Remote Connection & Chrome Path */}
<div
style={{
marginBottom: 4,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}>
<VSCodeCheckbox
checked={localBrowserSettings.remoteBrowserEnabled}
onChange={(e) => updateRemoteBrowserEnabled((e.target as HTMLInputElement).checked)}>
Use remote browser connection
</VSCodeCheckbox>
<ConnectionStatusIndicator
isChecking={isCheckingConnection}
isConnected={connectionStatus}
remoteBrowserEnabled={localBrowserSettings.remoteBrowserEnabled}
/>
</div>
<p
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
margin: "0 0 6px 0px",
}}>
Enable Cline to use your Chrome
{isBundled
? "(not detected on your machine)"
: detectedChromePath
? ` (${detectedChromePath})`
: ""}
. You can specify a custom path below. Using a remote browser connection requires starting Chrome
in debug mode
{localBrowserSettings.remoteBrowserEnabled ? (
<>
{" "}
manually (<code>--remote-debugging-port=9222</code>) or using the button below. Enter the
host address or leave it blank for automatic discovery.
</>
) : (
"."
)}
</p>
{/* Moved remote-specific settings to appear directly after enabling remote connection */}
{localBrowserSettings.remoteBrowserEnabled && (
<div style={{ marginLeft: 0, marginTop: 8 }}>
<VSCodeTextField
value={localBrowserSettings.remoteBrowserHost || ""}
placeholder="http://localhost:9222"
style={{ width: "100%", marginBottom: 8 }}
onChange={(e: any) => updateRemoteBrowserHost(e.target.value || undefined)}
/>
{shouldShowRelaunchButton && (
<div style={{ display: "flex", gap: "10px", marginBottom: 8, justifyContent: "center" }}>
<VSCodeButton
style={{ flex: 1 }}
disabled={debugMode}
onClick={relaunchChromeDebugMode}>
{debugMode ? "Launching Browser..." : "Launch Browser with Debug Mode"}
</VSCodeButton>
</div>
)}
{relaunchResult && (
<div
style={{
padding: "8px",
marginBottom: "8px",
backgroundColor: relaunchResult.success
? "rgba(0, 128, 0, 0.1)"
: "rgba(255, 0, 0, 0.1)",
color: relaunchResult.success
? "var(--vscode-terminal-ansiGreen)"
: "var(--vscode-terminal-ansiRed)",
borderRadius: "3px",
fontSize: "11px",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
}}>
{relaunchResult.message}
</div>
)}
<p
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
margin: 0,
}}></p>
</div>
)}
{/* Chrome Executable Path section now follows remote-specific settings */}
<div style={{ marginBottom: 8, marginTop: 8 }}>
<label
htmlFor="chrome-executable-path"
style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>
Chrome Executable Path (Optional)
</label>
<VSCodeTextField
id="chrome-executable-path"
value={localChromePath}
placeholder="e.g., /usr/bin/google-chrome or C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
style={{ width: "100%" }}
onChange={(e: any) => {
const newValue = e.target.value || ""
updateChromeExecutablePath(newValue)
}}
/>
<p
style={{
fontSize: "12px",
color: "var(--vscode-descriptionForeground)",
margin: "4px 0 0 0",
}}>
Leave blank to auto-detect.
</p>
</div>
</div>
</CollapsibleContent>
</div>
</CollapsibleContent>
</Section>
</div>
)
}
@@ -0,0 +1,114 @@
import { VSCodeCheckbox, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { memo } from "react"
import { OpenAIReasoningEffort } from "@shared/ChatSettings"
import Section from "../Section"
interface FeatureSettingsSectionProps {
renderSectionHeader: (tabId: string) => JSX.Element | null
}
const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionProps) => {
const {
enableCheckpointsSetting,
setEnableCheckpointsSetting,
mcpMarketplaceEnabled,
setMcpMarketplaceEnabled,
mcpRichDisplayEnabled,
setMcpRichDisplayEnabled,
mcpResponsesCollapsed,
setMcpResponsesCollapsed,
chatSettings,
setChatSettings,
} = useExtensionState()
return (
<div>
{renderSectionHeader("features")}
<Section>
<div style={{ marginBottom: 20 }}>
<div>
<VSCodeCheckbox
checked={enableCheckpointsSetting}
onChange={(e: any) => {
const checked = e.target.checked === true
setEnableCheckpointsSetting(checked)
}}>
Enable Checkpoints
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
Enables extension to save checkpoints of workspace throughout the task. Uses git under the hood which
may not work well with large workspaces.
</p>
</div>
<div style={{ marginTop: 10 }}>
<VSCodeCheckbox
checked={mcpMarketplaceEnabled}
onChange={(e: any) => {
const checked = e.target.checked === true
setMcpMarketplaceEnabled(checked)
}}>
Enable MCP Marketplace
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
Enables the MCP Marketplace tab for discovering and installing MCP servers.
</p>
</div>
<div style={{ marginTop: 10 }}>
<VSCodeCheckbox
checked={mcpRichDisplayEnabled}
onChange={(e: any) => {
const checked = e.target.checked === true
setMcpRichDisplayEnabled(checked)
}}>
Enable Rich MCP Display
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
Enables rich formatting for MCP responses. When disabled, responses will be shown in plain text.
</p>
</div>
<div style={{ marginTop: 10 }}>
<VSCodeCheckbox
checked={mcpResponsesCollapsed}
onChange={(e: any) => {
const checked = e.target.checked === true
setMcpResponsesCollapsed(checked)
}}>
Collapse MCP Responses
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
Sets the default display mode for MCP response panels
</p>
</div>
<div style={{ marginTop: 10 }}>
<label
htmlFor="openai-reasoning-effort-dropdown"
className="block text-sm font-medium text-[var(--vscode-foreground)] mb-1">
OpenAI Reasoning Effort
</label>
<VSCodeDropdown
id="openai-reasoning-effort-dropdown"
currentValue={chatSettings.openAIReasoningEffort || "medium"}
onChange={(e: any) => {
const newValue = e.target.currentValue as OpenAIReasoningEffort
setChatSettings({
...chatSettings,
openAIReasoningEffort: newValue,
})
}}
className="w-full">
<VSCodeOption value="low">Low</VSCodeOption>
<VSCodeOption value="medium">Medium</VSCodeOption>
<VSCodeOption value="high">High</VSCodeOption>
</VSCodeDropdown>
<p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
Reasoning effort for the OpenAI family of models(applies to all OpenAI model providers)
</p>
</div>
</div>
</Section>
</div>
)
}
export default memo(FeatureSettingsSection)
@@ -7,7 +7,7 @@ import Section from "../Section"
interface GeneralSettingsSectionProps {
chatSettings: ChatSettings
setChatSettings: (settings: ChatSettings) => void
telemetrySetting: TelemetrySetting
telemetrySetting: string
setTelemetrySetting: (value: TelemetrySetting) => void
renderSectionHeader: (tabId: string) => JSX.Element | null
}
@@ -0,0 +1,156 @@
import React, { useState, useEffect } from "react"
import { VSCodeTextField, VSCodeCheckbox, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import TerminalOutputLineLimitSlider from "../TerminalOutputLineLimitSlider"
import { StateServiceClient } from "../../../services/grpc-client"
import { Int64, Int64Request } from "@shared/proto/common"
import Section from "../Section"
interface TerminalSettingsSectionProps {
renderSectionHeader: (tabId: string) => JSX.Element | null
}
export const TerminalSettingsSection: React.FC<TerminalSettingsSectionProps> = ({ renderSectionHeader }) => {
const {
shellIntegrationTimeout,
setShellIntegrationTimeout,
terminalReuseEnabled,
setTerminalReuseEnabled,
defaultTerminalProfile,
setDefaultTerminalProfile,
availableTerminalProfiles,
platform,
} = useExtensionState()
const [inputValue, setInputValue] = useState((shellIntegrationTimeout / 1000).toString())
const [inputError, setInputError] = useState<string | null>(null)
const handleTimeoutChange = (event: Event) => {
const target = event.target as HTMLInputElement
const value = target.value
setInputValue(value)
const seconds = parseFloat(value)
if (isNaN(seconds) || seconds <= 0) {
setInputError("Please enter a positive number")
return
}
setInputError(null)
const timeout = Math.round(seconds * 1000)
setShellIntegrationTimeout(timeout)
StateServiceClient.updateTerminalConnectionTimeout({
value: timeout,
} as Int64Request)
.then((response: Int64) => {
setShellIntegrationTimeout(response.value)
setInputValue((response.value / 1000).toString())
})
.catch((error) => {
console.error("Failed to update terminal connection timeout:", error)
})
}
const handleInputBlur = () => {
if (inputError) {
setInputValue((shellIntegrationTimeout / 1000).toString())
setInputError(null)
}
}
const handleTerminalReuseChange = (event: Event) => {
const target = event.target as HTMLInputElement
const checked = target.checked
setTerminalReuseEnabled(checked)
StateServiceClient.updateTerminalReuseEnabled({ value: checked } as any).catch((error) => {
console.error("Failed to update terminal reuse enabled:", error)
})
}
// Use any to avoid type conflicts between Event and FormEvent
const handleDefaultTerminalProfileChange = (event: any) => {
const target = event.target as HTMLSelectElement
const profileId = target.value
// Only update the local state, let the Save button handle the backend update
setDefaultTerminalProfile(profileId)
}
const profilesToShow = availableTerminalProfiles
return (
<div>
{renderSectionHeader("terminal")}
<Section>
<div id="terminal-settings-section" style={{ marginBottom: 20 }}>
<div style={{ marginBottom: 15 }}>
<label
htmlFor="default-terminal-profile"
style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>
Default Terminal Profile
</label>
<VSCodeDropdown
id="default-terminal-profile"
value={defaultTerminalProfile || "default"}
onChange={handleDefaultTerminalProfileChange}
style={{ width: "100%" }}>
{profilesToShow.map((profile) => (
<VSCodeOption key={profile.id} value={profile.id} title={profile.description}>
{profile.name}
</VSCodeOption>
))}
</VSCodeDropdown>
<p style={{ fontSize: "12px", color: "var(--vscode-descriptionForeground)", margin: "5px 0 0 0" }}>
Select the default terminal Cline will use. 'Default' uses your VSCode global setting.
</p>
</div>
<div style={{ marginBottom: 15 }}>
<div style={{ marginBottom: 8 }}>
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>
Shell integration timeout (seconds)
</label>
<div style={{ display: "flex", alignItems: "center" }}>
<VSCodeTextField
style={{ width: "100%" }}
value={inputValue}
placeholder="Enter timeout in seconds"
onChange={(event) => handleTimeoutChange(event as Event)}
onBlur={handleInputBlur}
/>
</div>
{inputError && (
<div style={{ color: "var(--vscode-errorForeground)", fontSize: "12px", marginTop: 5 }}>
{inputError}
</div>
)}
</div>
<p style={{ fontSize: "12px", color: "var(--vscode-descriptionForeground)", margin: 0 }}>
Set how long Cline waits for shell integration to activate before executing commands. Increase this
value if you experience terminal connection timeouts.
</p>
</div>
<div style={{ marginBottom: 15 }}>
<div style={{ display: "flex", alignItems: "center", marginBottom: 8 }}>
<VSCodeCheckbox
checked={terminalReuseEnabled ?? true}
onChange={(event) => handleTerminalReuseChange(event as Event)}>
Enable aggressive terminal reuse
</VSCodeCheckbox>
</div>
<p style={{ fontSize: "12px", color: "var(--vscode-descriptionForeground)", margin: 0 }}>
When enabled, Cline will reuse existing terminal windows that aren't in the current working directory.
Disable this if you experience issues with task lockout after a terminal command.
</p>
</div>
<TerminalOutputLineLimitSlider />
</div>
</Section>
</div>
)
}
export default TerminalSettingsSection