SettingsView.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import { VSCodeButton, VSCodeCheckbox, VSCodeLink, VSCodeTextArea } 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. const IS_DEV = false // FIXME: use flags when packaging
  8. type SettingsViewProps = {
  9. onDone: () => void
  10. }
  11. const SettingsView = ({ onDone }: SettingsViewProps) => {
  12. const {
  13. apiConfiguration,
  14. version,
  15. customInstructions,
  16. setCustomInstructions,
  17. alwaysAllowReadOnly,
  18. setAlwaysAllowReadOnly,
  19. openRouterModels,
  20. } = useExtensionState()
  21. const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
  22. const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
  23. const handleSubmit = () => {
  24. const apiValidationResult = validateApiConfiguration(apiConfiguration)
  25. const modelIdValidationResult = validateModelId(apiConfiguration, openRouterModels)
  26. setApiErrorMessage(apiValidationResult)
  27. setModelIdErrorMessage(modelIdValidationResult)
  28. if (!apiValidationResult && !modelIdValidationResult) {
  29. vscode.postMessage({ type: "apiConfiguration", apiConfiguration })
  30. vscode.postMessage({ type: "customInstructions", text: customInstructions })
  31. vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly })
  32. onDone()
  33. }
  34. }
  35. useEffect(() => {
  36. setApiErrorMessage(undefined)
  37. setModelIdErrorMessage(undefined)
  38. }, [apiConfiguration])
  39. // validate as soon as the component is mounted
  40. /*
  41. useEffect will use stale values of variables if they are not included in the dependency array. so trying to use useEffect with a dependency array of only one value for example will use any other variables' old values. In most cases you don't want this, and should opt to use react-use hooks.
  42. useEffect(() => {
  43. // uses someVar and anotherVar
  44. // eslint-disable-next-line react-hooks/exhaustive-deps
  45. }, [someVar])
  46. If we only want to run code once on mount we can use react-use's useEffectOnce or useMount
  47. */
  48. const handleResetState = () => {
  49. vscode.postMessage({ type: "resetState" })
  50. }
  51. return (
  52. <div
  53. style={{
  54. position: "fixed",
  55. top: 0,
  56. left: 0,
  57. right: 0,
  58. bottom: 0,
  59. padding: "10px 0px 0px 20px",
  60. display: "flex",
  61. flexDirection: "column",
  62. overflow: "hidden",
  63. }}>
  64. <div
  65. style={{
  66. display: "flex",
  67. justifyContent: "space-between",
  68. alignItems: "center",
  69. marginBottom: "17px",
  70. paddingRight: 17,
  71. }}>
  72. <h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>Settings</h3>
  73. <VSCodeButton onClick={handleSubmit}>Done</VSCodeButton>
  74. </div>
  75. <div
  76. style={{ flexGrow: 1, overflowY: "scroll", paddingRight: 8, display: "flex", flexDirection: "column" }}>
  77. <div style={{ marginBottom: 5 }}>
  78. <ApiOptions
  79. showModelOptions={true}
  80. apiErrorMessage={apiErrorMessage}
  81. modelIdErrorMessage={modelIdErrorMessage}
  82. />
  83. </div>
  84. <div style={{ marginBottom: 5 }}>
  85. <VSCodeTextArea
  86. value={customInstructions ?? ""}
  87. style={{ width: "100%" }}
  88. rows={4}
  89. placeholder={
  90. 'e.g. "Run unit tests at the end", "Use TypeScript with async/await", "Speak in Spanish"'
  91. }
  92. onInput={(e: any) => setCustomInstructions(e.target?.value ?? "")}>
  93. <span style={{ fontWeight: "500" }}>Custom Instructions</span>
  94. </VSCodeTextArea>
  95. <p
  96. style={{
  97. fontSize: "12px",
  98. marginTop: "5px",
  99. color: "var(--vscode-descriptionForeground)",
  100. }}>
  101. These instructions are added to the end of the system prompt sent with every request.
  102. </p>
  103. </div>
  104. <div style={{ marginBottom: 5 }}>
  105. <VSCodeCheckbox
  106. checked={alwaysAllowReadOnly}
  107. onChange={(e: any) => setAlwaysAllowReadOnly(e.target.checked)}>
  108. <span style={{ fontWeight: "500" }}>Always allow read-only operations</span>
  109. </VSCodeCheckbox>
  110. <p
  111. style={{
  112. fontSize: "12px",
  113. marginTop: "5px",
  114. color: "var(--vscode-descriptionForeground)",
  115. }}>
  116. When enabled, Cline will automatically read files, view directories, and inspect sites without
  117. requiring you to click the Allow button.
  118. </p>
  119. </div>
  120. {IS_DEV && (
  121. <>
  122. <div style={{ marginTop: "10px", marginBottom: "4px" }}>Debug</div>
  123. <VSCodeButton onClick={handleResetState} style={{ marginTop: "5px", width: "auto" }}>
  124. Reset State
  125. </VSCodeButton>
  126. <p
  127. style={{
  128. fontSize: "12px",
  129. marginTop: "5px",
  130. color: "var(--vscode-descriptionForeground)",
  131. }}>
  132. This will reset all global state and secret storage in the extension.
  133. </p>
  134. </>
  135. )}
  136. <div
  137. style={{
  138. textAlign: "center",
  139. color: "var(--vscode-descriptionForeground)",
  140. fontSize: "12px",
  141. lineHeight: "1.2",
  142. marginTop: "auto",
  143. padding: "10px 8px 15px 0px",
  144. }}>
  145. <p style={{ wordWrap: "break-word", margin: 0, padding: 0 }}>
  146. If you have any questions or feedback, feel free to open an issue at{" "}
  147. <VSCodeLink href="https://github.com/saoudrizwan/claude-dev" style={{ display: "inline" }}>
  148. https://github.com/saoudrizwan/claude-dev
  149. </VSCodeLink>
  150. </p>
  151. <p style={{ fontStyle: "italic", margin: "10px 0 0 0", padding: 0 }}>v{version}</p>
  152. </div>
  153. </div>
  154. </div>
  155. )
  156. }
  157. export default memo(SettingsView)