SettingsView.tsx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. import { VSCodeButton, VSCodeCheckbox, VSCodeLink, VSCodeTextArea, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
  2. import { memo, useEffect, useState } from "react"
  3. import { useExtensionState } from "../../context/ExtensionStateContext"
  4. import { validateApiConfiguration, validateModelId } from "../../utils/validate"
  5. import { vscode } from "../../utils/vscode"
  6. import ApiOptions from "./ApiOptions"
  7. import McpEnabledToggle from "../mcp/McpEnabledToggle"
  8. import ApiConfigManager from "./ApiConfigManager"
  9. import { Mode } from "../../../../src/shared/modes"
  10. const IS_DEV = false // FIXME: use flags when packaging
  11. type SettingsViewProps = {
  12. onDone: () => void
  13. }
  14. const SettingsView = ({ onDone }: SettingsViewProps) => {
  15. const {
  16. apiConfiguration,
  17. version,
  18. customInstructions,
  19. setCustomInstructions,
  20. alwaysAllowReadOnly,
  21. setAlwaysAllowReadOnly,
  22. alwaysAllowWrite,
  23. setAlwaysAllowWrite,
  24. alwaysAllowExecute,
  25. setAlwaysAllowExecute,
  26. alwaysAllowBrowser,
  27. setAlwaysAllowBrowser,
  28. alwaysAllowMcp,
  29. setAlwaysAllowMcp,
  30. soundEnabled,
  31. setSoundEnabled,
  32. soundVolume,
  33. setSoundVolume,
  34. diffEnabled,
  35. setDiffEnabled,
  36. browserViewportSize,
  37. setBrowserViewportSize,
  38. openRouterModels,
  39. glamaModels,
  40. setAllowedCommands,
  41. allowedCommands,
  42. fuzzyMatchThreshold,
  43. setFuzzyMatchThreshold,
  44. preferredLanguage,
  45. setPreferredLanguage,
  46. writeDelayMs,
  47. setWriteDelayMs,
  48. screenshotQuality,
  49. setScreenshotQuality,
  50. terminalOutputLineLimit,
  51. setTerminalOutputLineLimit,
  52. mcpEnabled,
  53. alwaysApproveResubmit,
  54. setAlwaysApproveResubmit,
  55. requestDelaySeconds,
  56. setRequestDelaySeconds,
  57. currentApiConfigName,
  58. listApiConfigMeta,
  59. mode,
  60. setMode,
  61. } = useExtensionState()
  62. const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
  63. const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
  64. const [commandInput, setCommandInput] = useState("")
  65. const handleSubmit = () => {
  66. const apiValidationResult = validateApiConfiguration(apiConfiguration)
  67. const modelIdValidationResult = validateModelId(apiConfiguration, glamaModels, openRouterModels)
  68. setApiErrorMessage(apiValidationResult)
  69. setModelIdErrorMessage(modelIdValidationResult)
  70. if (!apiValidationResult && !modelIdValidationResult) {
  71. vscode.postMessage({
  72. type: "apiConfiguration",
  73. apiConfiguration
  74. })
  75. vscode.postMessage({ type: "customInstructions", text: customInstructions })
  76. vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly })
  77. vscode.postMessage({ type: "alwaysAllowWrite", bool: alwaysAllowWrite })
  78. vscode.postMessage({ type: "alwaysAllowExecute", bool: alwaysAllowExecute })
  79. vscode.postMessage({ type: "alwaysAllowBrowser", bool: alwaysAllowBrowser })
  80. vscode.postMessage({ type: "alwaysAllowMcp", bool: alwaysAllowMcp })
  81. vscode.postMessage({ type: "allowedCommands", commands: allowedCommands ?? [] })
  82. vscode.postMessage({ type: "soundEnabled", bool: soundEnabled })
  83. vscode.postMessage({ type: "soundVolume", value: soundVolume })
  84. vscode.postMessage({ type: "diffEnabled", bool: diffEnabled })
  85. vscode.postMessage({ type: "browserViewportSize", text: browserViewportSize })
  86. vscode.postMessage({ type: "fuzzyMatchThreshold", value: fuzzyMatchThreshold ?? 1.0 })
  87. vscode.postMessage({ type: "preferredLanguage", text: preferredLanguage })
  88. vscode.postMessage({ type: "writeDelayMs", value: writeDelayMs })
  89. vscode.postMessage({ type: "screenshotQuality", value: screenshotQuality ?? 75 })
  90. vscode.postMessage({ type: "terminalOutputLineLimit", value: terminalOutputLineLimit ?? 500 })
  91. vscode.postMessage({ type: "mcpEnabled", bool: mcpEnabled })
  92. vscode.postMessage({ type: "alwaysApproveResubmit", bool: alwaysApproveResubmit })
  93. vscode.postMessage({ type: "requestDelaySeconds", value: requestDelaySeconds })
  94. vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName })
  95. vscode.postMessage({
  96. type: "upsertApiConfiguration",
  97. text: currentApiConfigName,
  98. apiConfiguration
  99. })
  100. vscode.postMessage({ type: "mode", text: mode })
  101. onDone()
  102. }
  103. }
  104. useEffect(() => {
  105. setApiErrorMessage(undefined)
  106. setModelIdErrorMessage(undefined)
  107. }, [apiConfiguration])
  108. // Initial validation on mount
  109. useEffect(() => {
  110. const apiValidationResult = validateApiConfiguration(apiConfiguration)
  111. const modelIdValidationResult = validateModelId(apiConfiguration, glamaModels, openRouterModels)
  112. setApiErrorMessage(apiValidationResult)
  113. setModelIdErrorMessage(modelIdValidationResult)
  114. }, [apiConfiguration, glamaModels, openRouterModels])
  115. const handleResetState = () => {
  116. vscode.postMessage({ type: "resetState" })
  117. }
  118. const handleAddCommand = () => {
  119. const currentCommands = allowedCommands ?? []
  120. if (commandInput && !currentCommands.includes(commandInput)) {
  121. const newCommands = [...currentCommands, commandInput]
  122. setAllowedCommands(newCommands)
  123. setCommandInput("")
  124. vscode.postMessage({
  125. type: "allowedCommands",
  126. commands: newCommands
  127. })
  128. }
  129. }
  130. return (
  131. <div
  132. style={{
  133. position: "fixed",
  134. top: 0,
  135. left: 0,
  136. right: 0,
  137. bottom: 0,
  138. padding: "10px 0px 0px 20px",
  139. display: "flex",
  140. flexDirection: "column",
  141. overflow: "hidden",
  142. }}>
  143. <div
  144. style={{
  145. display: "flex",
  146. justifyContent: "space-between",
  147. alignItems: "center",
  148. marginBottom: "17px",
  149. paddingRight: 17,
  150. }}>
  151. <h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>Settings</h3>
  152. <VSCodeButton onClick={handleSubmit}>Done</VSCodeButton>
  153. </div>
  154. <div
  155. style={{ flexGrow: 1, overflowY: "scroll", paddingRight: 8, display: "flex", flexDirection: "column" }}>
  156. <div style={{ marginBottom: 5 }}>
  157. <h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Provider Settings</h3>
  158. <ApiConfigManager
  159. currentApiConfigName={currentApiConfigName}
  160. listApiConfigMeta={listApiConfigMeta}
  161. onSelectConfig={(configName: string) => {
  162. vscode.postMessage({
  163. type: "loadApiConfiguration",
  164. text: configName
  165. })
  166. }}
  167. onDeleteConfig={(configName: string) => {
  168. vscode.postMessage({
  169. type: "deleteApiConfiguration",
  170. text: configName
  171. })
  172. }}
  173. onRenameConfig={(oldName: string, newName: string) => {
  174. vscode.postMessage({
  175. type: "renameApiConfiguration",
  176. values: { oldName, newName },
  177. apiConfiguration
  178. })
  179. }}
  180. onUpsertConfig={(configName: string) => {
  181. vscode.postMessage({
  182. type: "upsertApiConfiguration",
  183. text: configName,
  184. apiConfiguration
  185. })
  186. }}
  187. />
  188. <ApiOptions
  189. apiErrorMessage={apiErrorMessage}
  190. modelIdErrorMessage={modelIdErrorMessage}
  191. />
  192. </div>
  193. <div style={{ marginBottom: 5 }}>
  194. <div style={{ marginBottom: 15 }}>
  195. <h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Agent Settings</h3>
  196. <div style={{ marginBottom: 15 }}>
  197. <label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Agent Mode</label>
  198. <select
  199. value={mode}
  200. onChange={(e) => {
  201. const value = e.target.value as Mode
  202. setMode(value)
  203. vscode.postMessage({ type: "mode", text: value })
  204. }}
  205. style={{
  206. width: "100%",
  207. padding: "4px 8px",
  208. backgroundColor: "var(--vscode-input-background)",
  209. color: "var(--vscode-input-foreground)",
  210. border: "1px solid var(--vscode-input-border)",
  211. borderRadius: "2px",
  212. height: "28px"
  213. }}>
  214. <option value="code">Code</option>
  215. <option value="architect">Architect</option>
  216. <option value="ask">Ask</option>
  217. </select>
  218. <p style={{
  219. fontSize: "12px",
  220. marginTop: "5px",
  221. color: "var(--vscode-descriptionForeground)",
  222. }}>
  223. 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.
  224. </p>
  225. </div>
  226. <label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Preferred Language</label>
  227. <select
  228. value={preferredLanguage}
  229. onChange={(e) => setPreferredLanguage(e.target.value)}
  230. style={{
  231. width: "100%",
  232. padding: "4px 8px",
  233. backgroundColor: "var(--vscode-input-background)",
  234. color: "var(--vscode-input-foreground)",
  235. border: "1px solid var(--vscode-input-border)",
  236. borderRadius: "2px",
  237. height: "28px"
  238. }}>
  239. <option value="English">English</option>
  240. <option value="Arabic">Arabic - العربية</option>
  241. <option value="Brazilian Portuguese">Portuguese - Português (Brasil)</option>
  242. <option value="Czech">Czech - Čeština</option>
  243. <option value="French">French - Français</option>
  244. <option value="German">German - Deutsch</option>
  245. <option value="Hindi">Hindi - हिन्दी</option>
  246. <option value="Hungarian">Hungarian - Magyar</option>
  247. <option value="Italian">Italian - Italiano</option>
  248. <option value="Japanese">Japanese - 日本語</option>
  249. <option value="Korean">Korean - 한국어</option>
  250. <option value="Polish">Polish - Polski</option>
  251. <option value="Portuguese">Portuguese - Português (Portugal)</option>
  252. <option value="Russian">Russian - Русский</option>
  253. <option value="Simplified Chinese">Simplified Chinese - 简体中文</option>
  254. <option value="Spanish">Spanish - Español</option>
  255. <option value="Traditional Chinese">Traditional Chinese - 繁體中文</option>
  256. <option value="Turkish">Turkish - Türkçe</option>
  257. </select>
  258. <p style={{
  259. fontSize: "12px",
  260. marginTop: "5px",
  261. color: "var(--vscode-descriptionForeground)",
  262. }}>
  263. Select the language that Cline should use for communication.
  264. </p>
  265. </div>
  266. <VSCodeTextArea
  267. value={customInstructions ?? ""}
  268. style={{ width: "100%" }}
  269. rows={4}
  270. placeholder={
  271. 'e.g. "Run unit tests at the end", "Use TypeScript with async/await", "Speak in Spanish"'
  272. }
  273. onInput={(e: any) => setCustomInstructions(e.target?.value ?? "")}>
  274. <span style={{ fontWeight: "500" }}>Custom Instructions</span>
  275. </VSCodeTextArea>
  276. <p
  277. style={{
  278. fontSize: "12px",
  279. marginTop: "5px",
  280. color: "var(--vscode-descriptionForeground)",
  281. }}>
  282. These instructions are added to the end of the system prompt sent with every request. Custom instructions set in .clinerules and .cursorrules in the working directory are also included.
  283. </p>
  284. <McpEnabledToggle />
  285. </div>
  286. <div style={{ marginBottom: 5 }}>
  287. <div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
  288. <span style={{ fontWeight: "500", minWidth: '150px' }}>Terminal output limit</span>
  289. <input
  290. type="range"
  291. min="100"
  292. max="5000"
  293. step="100"
  294. value={terminalOutputLineLimit ?? 500}
  295. onChange={(e) => setTerminalOutputLineLimit(parseInt(e.target.value))}
  296. style={{
  297. flexGrow: 1,
  298. accentColor: 'var(--vscode-button-background)',
  299. height: '2px'
  300. }}
  301. />
  302. <span style={{ minWidth: '45px', textAlign: 'left' }}>
  303. {terminalOutputLineLimit ?? 500}
  304. </span>
  305. </div>
  306. <p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
  307. Maximum number of lines to include in terminal output when executing commands. When exceeded lines will be removed from the middle, saving tokens.
  308. </p>
  309. </div>
  310. <div style={{ marginBottom: 5 }}>
  311. <VSCodeCheckbox checked={diffEnabled} onChange={(e: any) => setDiffEnabled(e.target.checked)}>
  312. <span style={{ fontWeight: "500" }}>Enable editing through diffs</span>
  313. </VSCodeCheckbox>
  314. <p
  315. style={{
  316. fontSize: "12px",
  317. marginTop: "5px",
  318. color: "var(--vscode-descriptionForeground)",
  319. }}>
  320. 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.
  321. </p>
  322. {diffEnabled && (
  323. <div style={{ marginTop: 10 }}>
  324. <div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
  325. <span style={{ fontWeight: "500", minWidth: '100px' }}>Match precision</span>
  326. <input
  327. type="range"
  328. min="0.8"
  329. max="1"
  330. step="0.005"
  331. value={fuzzyMatchThreshold ?? 1.0}
  332. onChange={(e) => {
  333. setFuzzyMatchThreshold(parseFloat(e.target.value));
  334. }}
  335. style={{
  336. flexGrow: 1,
  337. accentColor: 'var(--vscode-button-background)',
  338. height: '2px'
  339. }}
  340. />
  341. <span style={{ minWidth: '35px', textAlign: 'left' }}>
  342. {Math.round((fuzzyMatchThreshold || 1) * 100)}%
  343. </span>
  344. </div>
  345. <p style={{ fontSize: "12px", marginBottom: 10, color: "var(--vscode-descriptionForeground)" }}>
  346. 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.
  347. </p>
  348. </div>
  349. )}
  350. </div>
  351. <div style={{ marginBottom: 5 }}>
  352. <VSCodeCheckbox
  353. checked={alwaysAllowReadOnly}
  354. onChange={(e: any) => setAlwaysAllowReadOnly(e.target.checked)}>
  355. <span style={{ fontWeight: "500" }}>Always approve read-only operations</span>
  356. </VSCodeCheckbox>
  357. <p
  358. style={{
  359. fontSize: "12px",
  360. marginTop: "5px",
  361. color: "var(--vscode-descriptionForeground)",
  362. }}>
  363. When enabled, Cline will automatically view directory contents and read files without requiring
  364. you to click the Approve button.
  365. </p>
  366. </div>
  367. <div style={{ marginBottom: 15, border: "2px solid var(--vscode-errorForeground)", borderRadius: "4px", padding: "10px" }}>
  368. <h4 style={{ fontWeight: 500, margin: "0 0 10px 0", color: "var(--vscode-errorForeground)" }}>⚠️ High-Risk Auto-Approve Settings</h4>
  369. <p style={{ fontSize: "12px", marginBottom: 15, color: "var(--vscode-descriptionForeground)" }}>
  370. The following settings allow Cline to automatically perform potentially dangerous operations without requiring approval.
  371. Enable these settings only if you fully trust the AI and understand the associated security risks.
  372. </p>
  373. <div style={{ marginBottom: 5 }}>
  374. <VSCodeCheckbox
  375. checked={alwaysAllowWrite}
  376. onChange={(e: any) => setAlwaysAllowWrite(e.target.checked)}>
  377. <span style={{ fontWeight: "500" }}>Always approve write operations</span>
  378. </VSCodeCheckbox>
  379. <p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
  380. Automatically create and edit files without requiring approval
  381. </p>
  382. {alwaysAllowWrite && (
  383. <div style={{ marginTop: 10 }}>
  384. <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
  385. <input
  386. type="range"
  387. min="0"
  388. max="5000"
  389. step="100"
  390. value={writeDelayMs}
  391. onChange={(e) => setWriteDelayMs(parseInt(e.target.value))}
  392. style={{
  393. flex: 1,
  394. accentColor: 'var(--vscode-button-background)',
  395. height: '2px'
  396. }}
  397. />
  398. <span style={{ minWidth: '45px', textAlign: 'left' }}>
  399. {writeDelayMs}ms
  400. </span>
  401. </div>
  402. <p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
  403. Delay after writes to allow diagnostics to detect potential problems
  404. </p>
  405. </div>
  406. )}
  407. </div>
  408. <div style={{ marginBottom: 5 }}>
  409. <VSCodeCheckbox
  410. checked={alwaysAllowBrowser}
  411. onChange={(e: any) => setAlwaysAllowBrowser(e.target.checked)}>
  412. <span style={{ fontWeight: "500" }}>Always approve browser actions</span>
  413. </VSCodeCheckbox>
  414. <p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
  415. Automatically perform browser actions without requiring approval<br />
  416. Note: Only applies when the model supports computer use
  417. </p>
  418. </div>
  419. <div style={{ marginBottom: 5 }}>
  420. <VSCodeCheckbox
  421. checked={alwaysApproveResubmit}
  422. onChange={(e: any) => setAlwaysApproveResubmit(e.target.checked)}>
  423. <span style={{ fontWeight: "500" }}>Always retry failed API requests</span>
  424. </VSCodeCheckbox>
  425. <p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
  426. Automatically retry failed API requests when server returns an error response
  427. </p>
  428. {alwaysApproveResubmit && (
  429. <div style={{ marginTop: 10 }}>
  430. <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
  431. <input
  432. type="range"
  433. min="0"
  434. max="100"
  435. step="1"
  436. value={requestDelaySeconds}
  437. onChange={(e) => setRequestDelaySeconds(parseInt(e.target.value))}
  438. style={{
  439. flex: 1,
  440. accentColor: 'var(--vscode-button-background)',
  441. height: '2px'
  442. }}
  443. />
  444. <span style={{ minWidth: '45px', textAlign: 'left' }}>
  445. {requestDelaySeconds}s
  446. </span>
  447. </div>
  448. <p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
  449. Delay before retrying the request
  450. </p>
  451. </div>
  452. )}
  453. </div>
  454. <div style={{ marginBottom: 5 }}>
  455. <VSCodeCheckbox
  456. checked={alwaysAllowMcp}
  457. onChange={(e: any) => setAlwaysAllowMcp(e.target.checked)}>
  458. <span style={{ fontWeight: "500" }}>Always approve MCP tools</span>
  459. </VSCodeCheckbox>
  460. <p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
  461. Enable auto-approval of individual MCP tools in the MCP Servers view (requires both this setting and the tool's individual "Always allow" checkbox)
  462. </p>
  463. </div>
  464. <div style={{ marginBottom: 5 }}>
  465. <VSCodeCheckbox
  466. checked={alwaysAllowExecute}
  467. onChange={(e: any) => setAlwaysAllowExecute(e.target.checked)}>
  468. <span style={{ fontWeight: "500" }}>Always approve allowed execute operations</span>
  469. </VSCodeCheckbox>
  470. <p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
  471. Automatically execute allowed terminal commands without requiring approval
  472. </p>
  473. {alwaysAllowExecute && (
  474. <div style={{ marginTop: 10 }}>
  475. <span style={{ fontWeight: "500" }}>Allowed Auto-Execute Commands</span>
  476. <p style={{
  477. fontSize: "12px",
  478. marginTop: "5px",
  479. color: "var(--vscode-descriptionForeground)",
  480. }}>
  481. Command prefixes that can be auto-executed when "Always approve execute operations" is enabled.
  482. </p>
  483. <div style={{ display: 'flex', gap: '5px', marginTop: '10px' }}>
  484. <VSCodeTextField
  485. value={commandInput}
  486. onInput={(e: any) => setCommandInput(e.target.value)}
  487. onKeyDown={(e: any) => {
  488. if (e.key === 'Enter') {
  489. e.preventDefault()
  490. handleAddCommand()
  491. }
  492. }}
  493. placeholder="Enter command prefix (e.g., 'git ')"
  494. style={{ flexGrow: 1 }}
  495. />
  496. <VSCodeButton onClick={handleAddCommand}>
  497. Add
  498. </VSCodeButton>
  499. </div>
  500. <div style={{
  501. marginTop: '10px',
  502. display: 'flex',
  503. flexWrap: 'wrap',
  504. gap: '5px'
  505. }}>
  506. {(allowedCommands ?? []).map((cmd, index) => (
  507. <div key={index} style={{
  508. display: 'flex',
  509. alignItems: 'center',
  510. gap: '5px',
  511. backgroundColor: 'var(--vscode-button-secondaryBackground)',
  512. padding: '2px 6px',
  513. borderRadius: '4px',
  514. border: '1px solid var(--vscode-button-secondaryBorder)',
  515. height: '24px'
  516. }}>
  517. <span>{cmd}</span>
  518. <VSCodeButton
  519. appearance="icon"
  520. style={{
  521. padding: 0,
  522. margin: 0,
  523. height: '20px',
  524. width: '20px',
  525. minWidth: '20px',
  526. display: 'flex',
  527. alignItems: 'center',
  528. justifyContent: 'center'
  529. }}
  530. onClick={() => {
  531. const newCommands = (allowedCommands ?? []).filter((_, i) => i !== index)
  532. setAllowedCommands(newCommands)
  533. vscode.postMessage({
  534. type: "allowedCommands",
  535. commands: newCommands
  536. })
  537. }}
  538. >
  539. <span className="codicon codicon-close" />
  540. </VSCodeButton>
  541. </div>
  542. ))}
  543. </div>
  544. </div>
  545. )}
  546. </div>
  547. </div>
  548. <div style={{ marginBottom: 5 }}>
  549. <div style={{ marginBottom: 10 }}>
  550. <div style={{ marginBottom: 15 }}>
  551. <h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Browser Settings</h3>
  552. <label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Viewport size</label>
  553. <select
  554. value={browserViewportSize}
  555. onChange={(e) => setBrowserViewportSize(e.target.value)}
  556. style={{
  557. width: "100%",
  558. padding: "4px 8px",
  559. backgroundColor: "var(--vscode-input-background)",
  560. color: "var(--vscode-input-foreground)",
  561. border: "1px solid var(--vscode-input-border)",
  562. borderRadius: "2px",
  563. height: "28px"
  564. }}>
  565. <option value="1280x800">Large Desktop (1280x800)</option>
  566. <option value="900x600">Small Desktop (900x600)</option>
  567. <option value="768x1024">Tablet (768x1024)</option>
  568. <option value="360x640">Mobile (360x640)</option>
  569. </select>
  570. <p style={{
  571. fontSize: "12px",
  572. marginTop: "5px",
  573. color: "var(--vscode-descriptionForeground)",
  574. }}>
  575. Select the viewport size for browser interactions. This affects how websites are displayed and interacted with.
  576. </p>
  577. </div>
  578. <div style={{ marginBottom: 15 }}>
  579. <div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
  580. <span style={{ fontWeight: "500", minWidth: '100px' }}>Screenshot quality</span>
  581. <input
  582. type="range"
  583. min="1"
  584. max="100"
  585. step="1"
  586. value={screenshotQuality ?? 75}
  587. onChange={(e) => setScreenshotQuality(parseInt(e.target.value))}
  588. style={{
  589. flexGrow: 1,
  590. accentColor: 'var(--vscode-button-background)',
  591. height: '2px'
  592. }}
  593. />
  594. <span style={{ minWidth: '35px', textAlign: 'left' }}>
  595. {screenshotQuality ?? 75}%
  596. </span>
  597. </div>
  598. <p style={{
  599. fontSize: "12px",
  600. marginTop: "5px",
  601. color: "var(--vscode-descriptionForeground)",
  602. }}>
  603. Adjust the WebP quality of browser screenshots. Higher values provide clearer screenshots but increase token usage.
  604. </p>
  605. </div>
  606. </div>
  607. <div style={{ marginBottom: 5 }}>
  608. <div style={{ marginBottom: 10 }}>
  609. <h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Notification Settings</h3>
  610. <VSCodeCheckbox checked={soundEnabled} onChange={(e: any) => setSoundEnabled(e.target.checked)}>
  611. <span style={{ fontWeight: "500" }}>Enable sound effects</span>
  612. </VSCodeCheckbox>
  613. <p
  614. style={{
  615. fontSize: "12px",
  616. marginTop: "5px",
  617. color: "var(--vscode-descriptionForeground)",
  618. }}>
  619. When enabled, Cline will play sound effects for notifications and events.
  620. </p>
  621. </div>
  622. {soundEnabled && (
  623. <div style={{ marginLeft: 0 }}>
  624. <div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
  625. <span style={{ fontWeight: "500", minWidth: '100px' }}>Volume</span>
  626. <input
  627. type="range"
  628. min="0"
  629. max="1"
  630. step="0.01"
  631. value={soundVolume ?? 0.5}
  632. onChange={(e) => setSoundVolume(parseFloat(e.target.value))}
  633. style={{
  634. flexGrow: 1,
  635. accentColor: 'var(--vscode-button-background)',
  636. height: '2px'
  637. }}
  638. aria-label="Volume"
  639. />
  640. <span style={{ minWidth: '35px', textAlign: 'left' }}>
  641. {((soundVolume ?? 0.5) * 100).toFixed(0)}%
  642. </span>
  643. </div>
  644. </div>
  645. )}
  646. </div>
  647. </div>
  648. {IS_DEV && (
  649. <>
  650. <div style={{ marginTop: "10px", marginBottom: "4px" }}>Debug</div>
  651. <VSCodeButton onClick={handleResetState} style={{ marginTop: "5px", width: "auto" }}>
  652. Reset State
  653. </VSCodeButton>
  654. <p
  655. style={{
  656. fontSize: "12px",
  657. marginTop: "5px",
  658. color: "var(--vscode-descriptionForeground)",
  659. }}>
  660. This will reset all global state and secret storage in the extension.
  661. </p>
  662. </>
  663. )}
  664. <div
  665. style={{
  666. textAlign: "center",
  667. color: "var(--vscode-descriptionForeground)",
  668. fontSize: "12px",
  669. lineHeight: "1.2",
  670. marginTop: "auto",
  671. padding: "10px 8px 15px 0px",
  672. }}>
  673. <p style={{ wordWrap: "break-word", margin: 0, padding: 0 }}>
  674. If you have any questions or feedback, feel free to open an issue at{" "}
  675. <VSCodeLink href="https://github.com/RooVetGit/Roo-Cline" style={{ display: "inline" }}>
  676. github.com/RooVetGit/Roo-Cline
  677. </VSCodeLink> or join {" "}
  678. <VSCodeLink href="https://www.reddit.com/r/roocline/" style={{ display: "inline" }}>
  679. reddit.com/r/roocline
  680. </VSCodeLink>
  681. </p>
  682. <p style={{ fontStyle: "italic", margin: "10px 0 0 0", padding: 0 }}>v{version}</p>
  683. </div>
  684. </div>
  685. </div>
  686. )
  687. }
  688. export default memo(SettingsView)