mirror of
https://github.com/cline/cline.git
synced 2026-09-01 15:11:04 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e67bb6c636 | |||
| fa7794e9a6 | |||
| 88029834be | |||
| 1f267d058a | |||
| 853b8a6470 | |||
| a5258e46e1 | |||
| 36dfc7de11 |
@@ -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
|
||||
+173
-147
@@ -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
|
||||
Reference in New Issue
Block a user