Merge branch 'main' into aws-profile-support

This commit is contained in:
Abhishek Aryan
2025-01-20 20:29:43 +00:00
committed by GitHub
29 changed files with 770 additions and 1244 deletions

View File

@@ -714,11 +714,15 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
value={mode}
disabled={textAreaDisabled}
onChange={(e) => {
const newMode = e.target.value as Mode
setMode(newMode)
const value = e.target.value
if (value === "prompts-action") {
window.postMessage({ type: "action", action: "promptsButtonClicked" })
return
}
setMode(value as Mode)
vscode.postMessage({
type: "mode",
text: newMode,
text: value,
})
}}
style={{
@@ -737,6 +741,10 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
{mode.name}
</option>
))}
<option disabled style={{ borderTop: "1px solid var(--vscode-dropdown-border)" }}>
</option>
<option value="prompts-action">Edit...</option>
</select>
<div style={caretContainerStyle}>
<CaretIcon />
@@ -753,20 +761,25 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
overflow: "hidden",
}}>
<select
value={currentApiConfigName}
value={currentApiConfigName || ""}
disabled={textAreaDisabled}
onChange={(e) =>
onChange={(e) => {
const value = e.target.value
if (value === "settings-action") {
window.postMessage({ type: "action", action: "settingsButtonClicked" })
return
}
vscode.postMessage({
type: "loadApiConfiguration",
text: e.target.value,
text: value,
})
}
}}
style={{
...selectStyle,
width: "100%",
textOverflow: "ellipsis",
}}>
{(listApiConfigMeta || [])?.map((config) => (
{(listApiConfigMeta || []).map((config) => (
<option
key={config.name}
value={config.name}
@@ -777,6 +790,10 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
{config.name}
</option>
))}
<option disabled style={{ borderTop: "1px solid var(--vscode-dropdown-border)" }}>
</option>
<option value="settings-action">Edit...</option>
</select>
<div style={caretContainerStyle}>
<CaretIcon />
@@ -806,14 +823,18 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
role="button"
aria-label="enhance prompt"
data-testid="enhance-prompt-button"
className={`input-icon-button ${textAreaDisabled ? "disabled" : ""} codicon codicon-sparkle`}
className={`input-icon-button ${
textAreaDisabled ? "disabled" : ""
} codicon codicon-sparkle`}
onClick={() => !textAreaDisabled && handleEnhancePrompt()}
style={{ fontSize: 16.5 }}
/>
)}
</div>
<span
className={`input-icon-button ${shouldDisableImages ? "disabled" : ""} codicon codicon-device-camera`}
className={`input-icon-button ${
shouldDisableImages ? "disabled" : ""
} codicon codicon-device-camera`}
onClick={() => !shouldDisableImages && onSelectImages()}
style={{ fontSize: 16.5 }}
/>

View File

@@ -22,6 +22,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
mode,
customInstructions,
setCustomInstructions,
preferredLanguage,
setPreferredLanguage,
} = useExtensionState()
const [testPrompt, setTestPrompt] = useState("")
const [isEnhancing, setIsEnhancing] = useState(false)
@@ -146,6 +148,55 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
<div style={{ flex: 1, overflow: "auto", padding: "0 20px" }}>
<div style={{ marginBottom: "20px" }}>
<div style={{ marginBottom: "20px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>Preferred Language</div>
<select
value={preferredLanguage}
onChange={(e) => {
setPreferredLanguage(e.target.value)
vscode.postMessage({
type: "preferredLanguage",
text: e.target.value,
})
}}
style={{
width: "100%",
padding: "4px 8px",
backgroundColor: "var(--vscode-input-background)",
color: "var(--vscode-input-foreground)",
border: "1px solid var(--vscode-input-border)",
borderRadius: "2px",
height: "28px",
}}>
<option value="English">English</option>
<option value="Arabic">Arabic - العربية</option>
<option value="Brazilian Portuguese">Portuguese - Português (Brasil)</option>
<option value="Czech">Czech - Čeština</option>
<option value="French">French - Français</option>
<option value="German">German - Deutsch</option>
<option value="Hindi">Hindi - ि</option>
<option value="Hungarian">Hungarian - Magyar</option>
<option value="Italian">Italian - Italiano</option>
<option value="Japanese">Japanese - </option>
<option value="Korean">Korean - </option>
<option value="Polish">Polish - Polski</option>
<option value="Portuguese">Portuguese - Português (Portugal)</option>
<option value="Russian">Russian - Русский</option>
<option value="Simplified Chinese">Simplified Chinese - </option>
<option value="Spanish">Spanish - Español</option>
<option value="Traditional Chinese">Traditional Chinese - </option>
<option value="Turkish">Turkish - Türkçe</option>
</select>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
Select the language that Cline should use for communication.
</p>
</div>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>Custom Instructions for All Modes</div>
<div
style={{ fontSize: "13px", color: "var(--vscode-descriptionForeground)", marginBottom: "8px" }}>

View File

@@ -45,7 +45,7 @@ interface ApiOptionsProps {
}
const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) => {
const { apiConfiguration, setApiConfiguration, uriScheme, onUpdateApiConfig } = useExtensionState()
const { apiConfiguration, setApiConfiguration, uriScheme, handleInputChange } = useExtensionState()
const [ollamaModels, setOllamaModels] = useState<string[]>([])
const [lmStudioModels, setLmStudioModels] = useState<string[]>([])
const [vsCodeLmModels, setVsCodeLmModels] = useState<vscodemodels.LanguageModelChatSelector[]>([])
@@ -53,12 +53,6 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion)
const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => {
const apiConfig = { ...apiConfiguration, [field]: event.target.value }
onUpdateApiConfig(apiConfig)
setApiConfiguration(apiConfig)
}
const { selectedProvider, selectedModelId, selectedModelInfo } = useMemo(() => {
return normalizeApiConfiguration(apiConfiguration)
}, [apiConfiguration])
@@ -162,7 +156,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.apiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("apiKey")}
onChange={handleInputChange("apiKey")}
placeholder="Enter API Key...">
<span style={{ fontWeight: 500 }}>Anthropic API Key</span>
</VSCodeTextField>
@@ -183,7 +177,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.anthropicBaseUrl || ""}
style={{ width: "100%", marginTop: 3 }}
type="url"
onInput={handleInputChange("anthropicBaseUrl")}
onChange={handleInputChange("anthropicBaseUrl")}
placeholder="Default: https://api.anthropic.com"
/>
)}
@@ -212,7 +206,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.glamaApiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("glamaApiKey")}
onChange={handleInputChange("glamaApiKey")}
placeholder="Enter API Key...">
<span style={{ fontWeight: 500 }}>Glama API Key</span>
</VSCodeTextField>
@@ -241,7 +235,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.openAiNativeApiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("openAiNativeApiKey")}
onChange={handleInputChange("openAiNativeApiKey")}
placeholder="Enter API Key...">
<span style={{ fontWeight: 500 }}>OpenAI API Key</span>
</VSCodeTextField>
@@ -269,7 +263,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.mistralApiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("mistralApiKey")}
onChange={handleInputChange("mistralApiKey")}
placeholder="Enter API Key...">
<span style={{ fontWeight: 500 }}>Mistral API Key</span>
</VSCodeTextField>
@@ -300,7 +294,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.openRouterApiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("openRouterApiKey")}
onChange={handleInputChange("openRouterApiKey")}
placeholder="Enter API Key...">
<span style={{ fontWeight: 500 }}>OpenRouter API Key</span>
</VSCodeTextField>
@@ -454,7 +448,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
<VSCodeTextField
value={apiConfiguration?.vertexProjectId || ""}
style={{ width: "100%" }}
onInput={handleInputChange("vertexProjectId")}
onChange={handleInputChange("vertexProjectId")}
placeholder="Enter Project ID...">
<span style={{ fontWeight: 500 }}>Google Cloud Project ID</span>
</VSCodeTextField>
@@ -512,7 +506,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.geminiApiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("geminiApiKey")}
onChange={handleInputChange("geminiApiKey")}
placeholder="Enter API Key...">
<span style={{ fontWeight: 500 }}>Gemini API Key</span>
</VSCodeTextField>
@@ -540,7 +534,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.openAiBaseUrl || ""}
style={{ width: "100%" }}
type="url"
onInput={handleInputChange("openAiBaseUrl")}
onChange={handleInputChange("openAiBaseUrl")}
placeholder={"Enter base URL..."}>
<span style={{ fontWeight: 500 }}>Base URL</span>
</VSCodeTextField>
@@ -548,7 +542,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.openAiApiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("openAiApiKey")}
onChange={handleInputChange("openAiApiKey")}
placeholder="Enter API Key...">
<span style={{ fontWeight: 500 }}>API Key</span>
</VSCodeTextField>
@@ -578,7 +572,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
<VSCodeTextField
value={apiConfiguration?.azureApiVersion || ""}
style={{ width: "100%", marginTop: 3 }}
onInput={handleInputChange("azureApiVersion")}
onChange={handleInputChange("azureApiVersion")}
placeholder={`Default: ${azureOpenAiDefaultApiVersion}`}
/>
)}
@@ -602,14 +596,14 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.lmStudioBaseUrl || ""}
style={{ width: "100%" }}
type="url"
onInput={handleInputChange("lmStudioBaseUrl")}
onChange={handleInputChange("lmStudioBaseUrl")}
placeholder={"Default: http://localhost:1234"}>
<span style={{ fontWeight: 500 }}>Base URL (optional)</span>
</VSCodeTextField>
<VSCodeTextField
value={apiConfiguration?.lmStudioModelId || ""}
style={{ width: "100%" }}
onInput={handleInputChange("lmStudioModelId")}
onChange={handleInputChange("lmStudioModelId")}
placeholder={"e.g. meta-llama-3.1-8b-instruct"}>
<span style={{ fontWeight: 500 }}>Model ID</span>
</VSCodeTextField>
@@ -671,7 +665,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.deepSeekApiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("deepSeekApiKey")}
onChange={handleInputChange("deepSeekApiKey")}
placeholder="Enter API Key...">
<span style={{ fontWeight: 500 }}>DeepSeek API Key</span>
</VSCodeTextField>
@@ -761,14 +755,14 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
value={apiConfiguration?.ollamaBaseUrl || ""}
style={{ width: "100%" }}
type="url"
onInput={handleInputChange("ollamaBaseUrl")}
onChange={handleInputChange("ollamaBaseUrl")}
placeholder={"Default: http://localhost:11434"}>
<span style={{ fontWeight: 500 }}>Base URL (optional)</span>
</VSCodeTextField>
<VSCodeTextField
value={apiConfiguration?.ollamaModelId || ""}
style={{ width: "100%" }}
onInput={handleInputChange("ollamaModelId")}
onChange={handleInputChange("ollamaModelId")}
placeholder={"e.g. llama3.1"}>
<span style={{ fontWeight: 500 }}>Model ID</span>
</VSCodeTextField>

View File

@@ -167,9 +167,18 @@ const GlamaModelPicker: React.FC = () => {
placeholder="Search and select a model..."
value={searchTerm}
onInput={(e) => {
handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
const newModelId = (e.target as HTMLInputElement)?.value?.toLowerCase()
const apiConfig = {
...apiConfiguration,
openAiModelId: newModelId,
}
setApiConfiguration(apiConfig)
setSearchTerm(newModelId)
setIsDropdownVisible(true)
}}
onChange={(e) => {
handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
}}
onFocus={() => setIsDropdownVisible(true)}
onKeyDown={handleKeyDown}
style={{ width: "100%", zIndex: GLAMA_MODEL_PICKER_Z_INDEX, position: "relative" }}>

View File

@@ -25,8 +25,6 @@ const OpenAiModelPicker: React.FC = () => {
}
setApiConfiguration(apiConfig)
onUpdateApiConfig(apiConfig)
setSearchTerm(newModelId)
}
useEffect(() => {
@@ -161,9 +159,18 @@ const OpenAiModelPicker: React.FC = () => {
placeholder="Search and select a model..."
value={searchTerm}
onInput={(e) => {
handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
const newModelId = (e.target as HTMLInputElement)?.value?.toLowerCase()
const apiConfig = {
...apiConfiguration,
openAiModelId: newModelId,
}
setApiConfiguration(apiConfig)
setSearchTerm(newModelId)
setIsDropdownVisible(true)
}}
onChange={(e) => {
handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
}}
onFocus={() => setIsDropdownVisible(true)}
onKeyDown={handleKeyDown}
style={{ width: "100%", zIndex: OPENAI_MODEL_PICKER_Z_INDEX, position: "relative" }}>

View File

@@ -167,9 +167,18 @@ const OpenRouterModelPicker: React.FC = () => {
placeholder="Search and select a model..."
value={searchTerm}
onInput={(e) => {
handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
const newModelId = (e.target as HTMLInputElement)?.value?.toLowerCase()
const apiConfig = {
...apiConfiguration,
openAiModelId: newModelId,
}
setApiConfiguration(apiConfig)
setSearchTerm(newModelId)
setIsDropdownVisible(true)
}}
onChange={(e) => {
handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
}}
onFocus={() => setIsDropdownVisible(true)}
onKeyDown={handleKeyDown}
style={{ width: "100%", zIndex: OPENROUTER_MODEL_PICKER_Z_INDEX, position: "relative" }}>

View File

@@ -1,20 +1,10 @@
import {
VSCodeButton,
VSCodeCheckbox,
VSCodeLink,
VSCodeTextArea,
VSCodeTextField,
} from "@vscode/webview-ui-toolkit/react"
import { VSCodeButton, VSCodeCheckbox, VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { memo, useEffect, useState } from "react"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { validateApiConfiguration, validateModelId } from "../../utils/validate"
import { vscode } from "../../utils/vscode"
import ApiOptions from "./ApiOptions"
import McpEnabledToggle from "../mcp/McpEnabledToggle"
import ApiConfigManager from "./ApiConfigManager"
import { Mode } from "../../../../src/shared/modes"
const IS_DEV = false // FIXME: use flags when packaging
type SettingsViewProps = {
onDone: () => void
@@ -24,8 +14,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
const {
apiConfiguration,
version,
customInstructions,
setCustomInstructions,
alwaysAllowReadOnly,
setAlwaysAllowReadOnly,
alwaysAllowWrite,
@@ -50,8 +38,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
allowedCommands,
fuzzyMatchThreshold,
setFuzzyMatchThreshold,
preferredLanguage,
setPreferredLanguage,
writeDelayMs,
setWriteDelayMs,
screenshotQuality,
@@ -65,8 +51,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
setRequestDelaySeconds,
currentApiConfigName,
listApiConfigMeta,
mode,
setMode,
experimentalDiffStrategy,
setExperimentalDiffStrategy,
} = useExtensionState()
@@ -85,7 +69,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
type: "apiConfiguration",
apiConfiguration,
})
vscode.postMessage({ type: "customInstructions", text: customInstructions })
vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly })
vscode.postMessage({ type: "alwaysAllowWrite", bool: alwaysAllowWrite })
vscode.postMessage({ type: "alwaysAllowExecute", bool: alwaysAllowExecute })
@@ -97,7 +80,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
vscode.postMessage({ type: "diffEnabled", bool: diffEnabled })
vscode.postMessage({ type: "browserViewportSize", text: browserViewportSize })
vscode.postMessage({ type: "fuzzyMatchThreshold", value: fuzzyMatchThreshold ?? 1.0 })
vscode.postMessage({ type: "preferredLanguage", text: preferredLanguage })
vscode.postMessage({ type: "writeDelayMs", value: writeDelayMs })
vscode.postMessage({ type: "screenshotQuality", value: screenshotQuality ?? 75 })
vscode.postMessage({ type: "terminalOutputLineLimit", value: terminalOutputLineLimit ?? 500 })
@@ -110,7 +92,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
text: currentApiConfigName,
apiConfiguration,
})
vscode.postMessage({ type: "mode", text: mode })
vscode.postMessage({ type: "experimentalDiffStrategy", bool: experimentalDiffStrategy })
onDone()
}
@@ -172,291 +153,69 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
</div>
<div
style={{ flexGrow: 1, overflowY: "scroll", paddingRight: 8, display: "flex", flexDirection: "column" }}>
<div style={{ marginBottom: 5 }}>
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>
Provider Settings
</h3>
<ApiConfigManager
currentApiConfigName={currentApiConfigName}
listApiConfigMeta={listApiConfigMeta}
onSelectConfig={(configName: string) => {
vscode.postMessage({
type: "loadApiConfiguration",
text: configName,
})
}}
onDeleteConfig={(configName: string) => {
vscode.postMessage({
type: "deleteApiConfiguration",
text: configName,
})
}}
onRenameConfig={(oldName: string, newName: string) => {
vscode.postMessage({
type: "renameApiConfiguration",
values: { oldName, newName },
apiConfiguration,
})
}}
onUpsertConfig={(configName: string) => {
vscode.postMessage({
type: "upsertApiConfiguration",
text: configName,
apiConfiguration,
})
}}
/>
<ApiOptions apiErrorMessage={apiErrorMessage} modelIdErrorMessage={modelIdErrorMessage} />
</div>
<div style={{ marginBottom: 5 }}>
<div style={{ marginBottom: 40 }}>
<h3 style={{ color: "var(--vscode-foreground)", margin: "0 0 15px 0" }}>Provider Settings</h3>
<div style={{ marginBottom: 15 }}>
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>
Agent Settings
</h3>
<div style={{ marginBottom: 15 }}>
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Agent Mode</label>
<select
value={mode}
onChange={(e) => {
const value = e.target.value as Mode
setMode(value)
vscode.postMessage({ type: "mode", text: value })
}}
style={{
width: "100%",
padding: "4px 8px",
backgroundColor: "var(--vscode-input-background)",
color: "var(--vscode-input-foreground)",
border: "1px solid var(--vscode-input-border)",
borderRadius: "2px",
height: "28px",
}}>
<option value="code">Code</option>
<option value="architect">Architect</option>
<option value="ask">Ask</option>
</select>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
Select the mode that best fits your needs. Code mode focuses on implementation details,
Architect mode on high-level design, and Ask mode on asking questions about the
codebase.
</p>
</div>
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>
Preferred Language
</label>
<select
value={preferredLanguage}
onChange={(e) => setPreferredLanguage(e.target.value)}
style={{
width: "100%",
padding: "4px 8px",
backgroundColor: "var(--vscode-input-background)",
color: "var(--vscode-input-foreground)",
border: "1px solid var(--vscode-input-border)",
borderRadius: "2px",
height: "28px",
}}>
<option value="English">English</option>
<option value="Arabic">Arabic - العربية</option>
<option value="Brazilian Portuguese">Portuguese - Português (Brasil)</option>
<option value="Czech">Czech - Čeština</option>
<option value="French">French - Français</option>
<option value="German">German - Deutsch</option>
<option value="Hindi">Hindi - ि</option>
<option value="Hungarian">Hungarian - Magyar</option>
<option value="Italian">Italian - Italiano</option>
<option value="Japanese">Japanese - </option>
<option value="Korean">Korean - </option>
<option value="Polish">Polish - Polski</option>
<option value="Portuguese">Portuguese - Português (Portugal)</option>
<option value="Russian">Russian - Русский</option>
<option value="Simplified Chinese">Simplified Chinese - </option>
<option value="Spanish">Spanish - Español</option>
<option value="Traditional Chinese">Traditional Chinese - </option>
<option value="Turkish">Turkish - Türkçe</option>
</select>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
Select the language that Cline should use for communication.
</p>
</div>
<div style={{ marginBottom: 15 }}>
<span style={{ fontWeight: "500" }}>Custom Instructions</span>
<VSCodeTextArea
value={customInstructions ?? ""}
style={{ width: "100%" }}
rows={4}
placeholder={
'e.g. "Run unit tests at the end", "Use TypeScript with async/await", "Speak in Spanish"'
}
onInput={(e: any) => setCustomInstructions(e.target?.value ?? "")}
/>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
These instructions are added to the end of the system prompt sent with every request. Custom
instructions set in .clinerules in the working directory are also included. For
mode-specific instructions, use the{" "}
<span className="codicon codicon-notebook" style={{ fontSize: "10px" }}></span> Prompts tab
in the top menu.
</p>
</div>
<McpEnabledToggle />
</div>
<div style={{ marginBottom: 5 }}>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<span style={{ fontWeight: "500", minWidth: "150px" }}>Terminal output limit</span>
<input
type="range"
min="100"
max="5000"
step="100"
value={terminalOutputLineLimit ?? 500}
onChange={(e) => setTerminalOutputLineLimit(parseInt(e.target.value))}
style={{
flexGrow: 1,
accentColor: "var(--vscode-button-background)",
height: "2px",
<ApiConfigManager
currentApiConfigName={currentApiConfigName}
listApiConfigMeta={listApiConfigMeta}
onSelectConfig={(configName: string) => {
vscode.postMessage({
type: "loadApiConfiguration",
text: configName,
})
}}
onDeleteConfig={(configName: string) => {
vscode.postMessage({
type: "deleteApiConfiguration",
text: configName,
})
}}
onRenameConfig={(oldName: string, newName: string) => {
vscode.postMessage({
type: "renameApiConfiguration",
values: { oldName, newName },
apiConfiguration,
})
}}
onUpsertConfig={(configName: string) => {
vscode.postMessage({
type: "upsertApiConfiguration",
text: configName,
apiConfiguration,
})
}}
/>
<span style={{ minWidth: "45px", textAlign: "left" }}>{terminalOutputLineLimit ?? 500}</span>
<ApiOptions apiErrorMessage={apiErrorMessage} modelIdErrorMessage={modelIdErrorMessage} />
</div>
<p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
Maximum number of lines to include in terminal output when executing commands. When exceeded
lines will be removed from the middle, saving tokens.
</p>
</div>
<div style={{ marginBottom: 5 }}>
<VSCodeCheckbox
checked={diffEnabled}
onChange={(e: any) => {
setDiffEnabled(e.target.checked)
if (!e.target.checked) {
// Reset experimental strategy when diffs are disabled
setExperimentalDiffStrategy(false)
}
}}>
<span style={{ fontWeight: "500" }}>Enable editing through diffs</span>
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
When enabled, Cline will be able to edit files more quickly and will automatically reject
truncated full-file writes. Works best with the latest Claude 3.5 Sonnet model.
</p>
{diffEnabled && (
<div style={{ marginTop: 10 }}>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<span style={{ color: "var(--vscode-errorForeground)" }}></span>
<VSCodeCheckbox
checked={experimentalDiffStrategy}
onChange={(e: any) => setExperimentalDiffStrategy(e.target.checked)}>
<span style={{ fontWeight: "500" }}>Use experimental unified diff strategy</span>
</VSCodeCheckbox>
</div>
<p
style={{
fontSize: "12px",
marginBottom: 15,
color: "var(--vscode-descriptionForeground)",
}}>
Enable the experimental unified diff strategy. This strategy might reduce the number of
retries caused by model errors but may cause unexpected behavior or incorrect edits.
Only enable if you understand the risks and are willing to carefully review all changes.
</p>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<span style={{ fontWeight: "500", minWidth: "100px" }}>Match precision</span>
<input
type="range"
min="0.8"
max="1"
step="0.005"
value={fuzzyMatchThreshold ?? 1.0}
onChange={(e) => {
setFuzzyMatchThreshold(parseFloat(e.target.value))
}}
style={{
flexGrow: 1,
accentColor: "var(--vscode-button-background)",
height: "2px",
}}
/>
<span style={{ minWidth: "35px", textAlign: "left" }}>
{Math.round((fuzzyMatchThreshold || 1) * 100)}%
</span>
</div>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
This slider controls how precisely code sections must match when applying diffs. Lower
values allow more flexible matching but increase the risk of incorrect replacements. Use
values below 100% with extreme caution.
</p>
</div>
)}
</div>
<div style={{ marginBottom: 5 }}>
<VSCodeCheckbox
checked={alwaysAllowReadOnly}
onChange={(e: any) => setAlwaysAllowReadOnly(e.target.checked)}>
<span style={{ fontWeight: "500" }}>Always approve read-only operations</span>
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
When enabled, Cline will automatically view directory contents and read files without requiring
you to click the Approve button.
</p>
</div>
<div
style={{
marginBottom: 15,
border: "2px solid var(--vscode-errorForeground)",
borderRadius: "4px",
padding: "10px",
}}>
<h4 style={{ fontWeight: 500, margin: "0 0 10px 0", color: "var(--vscode-errorForeground)" }}>
High-Risk Auto-Approve Settings
</h4>
<div style={{ marginBottom: 40 }}>
<h3 style={{ color: "var(--vscode-foreground)", margin: "0 0 15px 0" }}>Auto-Approve Settings</h3>
<p style={{ fontSize: "12px", marginBottom: 15, color: "var(--vscode-descriptionForeground)" }}>
The following settings allow Cline to automatically perform potentially dangerous operations
without requiring approval. Enable these settings only if you fully trust the AI and understand
the associated security risks.
The following settings allow Cline to automatically perform operations without requiring
approval. Enable these settings only if you fully trust the AI and understand the associated
security risks.
</p>
<div style={{ marginBottom: 5 }}>
<div style={{ marginBottom: 15 }}>
<VSCodeCheckbox
checked={alwaysAllowReadOnly}
onChange={(e: any) => setAlwaysAllowReadOnly(e.target.checked)}>
<span style={{ fontWeight: "500" }}>Always approve read-only operations</span>
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
When enabled, Cline will automatically view directory contents and read files without
requiring you to click the Approve button.
</p>
</div>
<div style={{ marginBottom: 15 }}>
<VSCodeCheckbox
checked={alwaysAllowWrite}
onChange={(e: any) => setAlwaysAllowWrite(e.target.checked)}>
@@ -495,7 +254,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
)}
</div>
<div style={{ marginBottom: 5 }}>
<div style={{ marginBottom: 15 }}>
<VSCodeCheckbox
checked={alwaysAllowBrowser}
onChange={(e: any) => setAlwaysAllowBrowser(e.target.checked)}>
@@ -508,7 +267,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
</p>
</div>
<div style={{ marginBottom: 5 }}>
<div style={{ marginBottom: 15 }}>
<VSCodeCheckbox
checked={alwaysApproveResubmit}
onChange={(e: any) => setAlwaysApproveResubmit(e.target.checked)}>
@@ -559,7 +318,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
</p>
</div>
<div style={{ marginBottom: 5 }}>
<div style={{ marginBottom: 15 }}>
<VSCodeCheckbox
checked={alwaysAllowExecute}
onChange={(e: any) => setAlwaysAllowExecute(e.target.checked)}>
@@ -652,135 +411,218 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
</div>
</div>
<div style={{ marginBottom: 5 }}>
<div style={{ marginBottom: 10 }}>
<div style={{ marginBottom: 15 }}>
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>
Browser Settings
</h3>
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>
Viewport size
</label>
<select
value={browserViewportSize}
onChange={(e) => setBrowserViewportSize(e.target.value)}
style={{
width: "100%",
padding: "4px 8px",
backgroundColor: "var(--vscode-input-background)",
color: "var(--vscode-input-foreground)",
border: "1px solid var(--vscode-input-border)",
borderRadius: "2px",
height: "28px",
}}>
<option value="1280x800">Large Desktop (1280x800)</option>
<option value="900x600">Small Desktop (900x600)</option>
<option value="768x1024">Tablet (768x1024)</option>
<option value="360x640">Mobile (360x640)</option>
</select>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
Select the viewport size for browser interactions. This affects how websites are
displayed and interacted with.
</p>
</div>
<div style={{ marginBottom: 15 }}>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<span style={{ fontWeight: "500", minWidth: "100px" }}>Screenshot quality</span>
<input
type="range"
min="1"
max="100"
step="1"
value={screenshotQuality ?? 75}
onChange={(e) => setScreenshotQuality(parseInt(e.target.value))}
style={{
flexGrow: 1,
accentColor: "var(--vscode-button-background)",
height: "2px",
}}
/>
<span style={{ minWidth: "35px", textAlign: "left" }}>{screenshotQuality ?? 75}%</span>
</div>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
Adjust the WebP quality of browser screenshots. Higher values provide clearer
screenshots but increase token usage.
</p>
</div>
</div>
<div style={{ marginBottom: 5 }}>
<div style={{ marginBottom: 10 }}>
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>
Notification Settings
</h3>
<VSCodeCheckbox
checked={soundEnabled}
onChange={(e: any) => setSoundEnabled(e.target.checked)}>
<span style={{ fontWeight: "500" }}>Enable sound effects</span>
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
When enabled, Cline will play sound effects for notifications and events.
</p>
</div>
{soundEnabled && (
<div style={{ marginLeft: 0 }}>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<span style={{ fontWeight: "500", minWidth: "100px" }}>Volume</span>
<input
type="range"
min="0"
max="1"
step="0.01"
value={soundVolume ?? 0.5}
onChange={(e) => setSoundVolume(parseFloat(e.target.value))}
style={{
flexGrow: 1,
accentColor: "var(--vscode-button-background)",
height: "2px",
}}
aria-label="Volume"
/>
<span style={{ minWidth: "35px", textAlign: "left" }}>
{((soundVolume ?? 0.5) * 100).toFixed(0)}%
</span>
</div>
</div>
)}
</div>
</div>
{IS_DEV && (
<>
<div style={{ marginTop: "10px", marginBottom: "4px" }}>Debug</div>
<VSCodeButton onClick={handleResetState} style={{ marginTop: "5px", width: "auto" }}>
Reset State
</VSCodeButton>
<div style={{ marginBottom: 40 }}>
<h3 style={{ color: "var(--vscode-foreground)", margin: "0 0 15px 0" }}>Browser Settings</h3>
<div style={{ marginBottom: 15 }}>
<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Viewport size</label>
<select
value={browserViewportSize}
onChange={(e) => setBrowserViewportSize(e.target.value)}
style={{
width: "100%",
padding: "4px 8px",
backgroundColor: "var(--vscode-input-background)",
color: "var(--vscode-input-foreground)",
border: "1px solid var(--vscode-input-border)",
borderRadius: "2px",
height: "28px",
}}>
<option value="1280x800">Large Desktop (1280x800)</option>
<option value="900x600">Small Desktop (900x600)</option>
<option value="768x1024">Tablet (768x1024)</option>
<option value="360x640">Mobile (360x640)</option>
</select>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
This will reset all global state and secret storage in the extension.
Select the viewport size for browser interactions. This affects how websites are displayed
and interacted with.
</p>
</>
)}
</div>
<div style={{ marginBottom: 15 }}>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<span style={{ fontWeight: "500", minWidth: "100px" }}>Screenshot quality</span>
<input
type="range"
min="1"
max="100"
step="1"
value={screenshotQuality ?? 75}
onChange={(e) => setScreenshotQuality(parseInt(e.target.value))}
style={{
flexGrow: 1,
accentColor: "var(--vscode-button-background)",
height: "2px",
}}
/>
<span style={{ minWidth: "35px", textAlign: "left" }}>{screenshotQuality ?? 75}%</span>
</div>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
Adjust the WebP quality of browser screenshots. Higher values provide clearer screenshots
but increase token usage.
</p>
</div>
</div>
<div style={{ marginBottom: 40 }}>
<h3 style={{ color: "var(--vscode-foreground)", margin: "0 0 15px 0" }}>Notification Settings</h3>
<div style={{ marginBottom: 15 }}>
<VSCodeCheckbox checked={soundEnabled} onChange={(e: any) => setSoundEnabled(e.target.checked)}>
<span style={{ fontWeight: "500" }}>Enable sound effects</span>
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
When enabled, Cline will play sound effects for notifications and events.
</p>
</div>
{soundEnabled && (
<div style={{ marginLeft: 0 }}>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<span style={{ fontWeight: "500", minWidth: "100px" }}>Volume</span>
<input
type="range"
min="0"
max="1"
step="0.01"
value={soundVolume ?? 0.5}
onChange={(e) => setSoundVolume(parseFloat(e.target.value))}
style={{
flexGrow: 1,
accentColor: "var(--vscode-button-background)",
height: "2px",
}}
aria-label="Volume"
/>
<span style={{ minWidth: "35px", textAlign: "left" }}>
{((soundVolume ?? 0.5) * 100).toFixed(0)}%
</span>
</div>
</div>
)}
</div>
<div style={{ marginBottom: 40 }}>
<h3 style={{ color: "var(--vscode-foreground)", margin: "0 0 15px 0" }}>Advanced Settings</h3>
<div style={{ marginBottom: 15 }}>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<span style={{ fontWeight: "500", minWidth: "150px" }}>Terminal output limit</span>
<input
type="range"
min="100"
max="5000"
step="100"
value={terminalOutputLineLimit ?? 500}
onChange={(e) => setTerminalOutputLineLimit(parseInt(e.target.value))}
style={{
flexGrow: 1,
accentColor: "var(--vscode-button-background)",
height: "2px",
}}
/>
<span style={{ minWidth: "45px", textAlign: "left" }}>
{terminalOutputLineLimit ?? 500}
</span>
</div>
<p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
Maximum number of lines to include in terminal output when executing commands. When exceeded
lines will be removed from the middle, saving tokens.
</p>
</div>
<div style={{ marginBottom: 15 }}>
<VSCodeCheckbox
checked={diffEnabled}
onChange={(e: any) => {
setDiffEnabled(e.target.checked)
if (!e.target.checked) {
// Reset experimental strategy when diffs are disabled
setExperimentalDiffStrategy(false)
}
}}>
<span style={{ fontWeight: "500" }}>Enable editing through diffs</span>
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
When enabled, Cline will be able to edit files more quickly and will automatically reject
truncated full-file writes. Works best with the latest Claude 3.5 Sonnet model.
</p>
{diffEnabled && (
<div style={{ marginTop: 10 }}>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<span style={{ color: "var(--vscode-errorForeground)" }}>⚠️</span>
<VSCodeCheckbox
checked={experimentalDiffStrategy}
onChange={(e: any) => setExperimentalDiffStrategy(e.target.checked)}>
<span style={{ fontWeight: "500" }}>
Use experimental unified diff strategy
</span>
</VSCodeCheckbox>
</div>
<p
style={{
fontSize: "12px",
marginBottom: 15,
color: "var(--vscode-descriptionForeground)",
}}>
Enable the experimental unified diff strategy. This strategy might reduce the number
of retries caused by model errors but may cause unexpected behavior or incorrect
edits. Only enable if you understand the risks and are willing to carefully review
all changes.
</p>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<span style={{ fontWeight: "500", minWidth: "100px" }}>Match precision</span>
<input
type="range"
min="0.8"
max="1"
step="0.005"
value={fuzzyMatchThreshold ?? 1.0}
onChange={(e) => {
setFuzzyMatchThreshold(parseFloat(e.target.value))
}}
style={{
flexGrow: 1,
accentColor: "var(--vscode-button-background)",
height: "2px",
}}
/>
<span style={{ minWidth: "35px", textAlign: "left" }}>
{Math.round((fuzzyMatchThreshold || 1) * 100)}%
</span>
</div>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
This slider controls how precisely code sections must match when applying diffs.
Lower values allow more flexible matching but increase the risk of incorrect
replacements. Use values below 100% with extreme caution.
</p>
</div>
)}
</div>
</div>
<div
style={{
@@ -801,7 +643,25 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
reddit.com/r/roocline
</VSCodeLink>
</p>
<p style={{ fontStyle: "italic", margin: "10px 0 0 0", padding: 0 }}>v{version}</p>
<p style={{ fontStyle: "italic", margin: "10px 0 0 0", padding: 0, marginBottom: 100 }}>
v{version}
</p>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
This will reset all global state and secret storage in the extension.
</p>
<VSCodeButton
onClick={handleResetState}
appearance="secondary"
style={{ marginTop: "5px", width: "auto" }}>
Reset State
</VSCodeButton>
</div>
</div>
</div>