|
|
@@ -6,7 +6,24 @@ import {
|
|
|
VSCodeOption,
|
|
|
VSCodeTextArea,
|
|
|
} from "@vscode/webview-ui-toolkit/react"
|
|
|
-import { memo, useCallback, useEffect, useState } from "react"
|
|
|
+import { memo, useCallback, useEffect, useState, useRef } from "react"
|
|
|
+import {
|
|
|
+ Settings,
|
|
|
+ Webhook,
|
|
|
+ CheckCheck,
|
|
|
+ SquareMousePointer,
|
|
|
+ GitBranch,
|
|
|
+ Bell,
|
|
|
+ Database,
|
|
|
+ SquareTerminal,
|
|
|
+ FlaskConical,
|
|
|
+ Globe,
|
|
|
+ Info,
|
|
|
+ LucideIcon,
|
|
|
+} from "lucide-react"
|
|
|
+import HeroTooltip from "@/components/common/HeroTooltip"
|
|
|
+import SectionHeader from "./SectionHeader"
|
|
|
+import Section from "./Section"
|
|
|
import PreferredLanguageSetting from "./PreferredLanguageSetting" // Added import
|
|
|
import { OpenAIReasoningEffort } from "@shared/ChatSettings"
|
|
|
import { useExtensionState } from "@/context/ExtensionStateContext"
|
|
|
@@ -22,13 +39,91 @@ import FeatureSettingsSection from "./FeatureSettingsSection"
|
|
|
import BrowserSettingsSection from "./BrowserSettingsSection"
|
|
|
import TerminalSettingsSection from "./TerminalSettingsSection"
|
|
|
import { FEATURE_FLAGS } from "@shared/services/feature-flags/feature-flags"
|
|
|
+import { Tab, TabContent, TabHeader, TabList, TabTrigger } from "../common/Tab"
|
|
|
+import { cn } from "@/utils/cn"
|
|
|
const { IS_DEV } = process.env
|
|
|
|
|
|
+// Styles for the tab system
|
|
|
+const settingsTabsContainer = "flex flex-1 overflow-hidden [&.narrow_.tab-label]:hidden"
|
|
|
+const settingsTabList =
|
|
|
+ "w-48 data-[compact=true]:w-12 flex-shrink-0 flex flex-col overflow-y-auto overflow-x-hidden border-r border-[var(--vscode-sideBar-background)]"
|
|
|
+const settingsTabTrigger =
|
|
|
+ "whitespace-nowrap overflow-hidden min-w-0 h-12 px-4 py-3 box-border flex items-center border-l-2 border-transparent text-[var(--vscode-foreground)] opacity-70 bg-transparent hover:bg-[var(--vscode-list-hoverBackground)] data-[compact=true]:w-12 data-[compact=true]:p-4 cursor-pointer"
|
|
|
+const settingsTabTriggerActive =
|
|
|
+ "opacity-100 border-l-2 border-l-[var(--vscode-focusBorder)] border-t-0 border-r-0 border-b-0 bg-[var(--vscode-list-activeSelectionBackground)]"
|
|
|
+
|
|
|
+// Tab definitions
|
|
|
+interface SettingsTab {
|
|
|
+ id: string
|
|
|
+ name: string
|
|
|
+ tooltipText: string
|
|
|
+ headerText: string
|
|
|
+ icon: LucideIcon
|
|
|
+}
|
|
|
+
|
|
|
+export const SETTINGS_TABS: SettingsTab[] = [
|
|
|
+ {
|
|
|
+ id: "api-config",
|
|
|
+ name: "API Configuration",
|
|
|
+ tooltipText: "API Configuration",
|
|
|
+ headerText: "API Configuration",
|
|
|
+ icon: Webhook,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "general",
|
|
|
+ name: "General",
|
|
|
+ tooltipText: "General Settings",
|
|
|
+ headerText: "General Settings",
|
|
|
+ icon: Settings,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "features",
|
|
|
+ name: "Features",
|
|
|
+ tooltipText: "Feature Settings",
|
|
|
+ headerText: "Feature Settings",
|
|
|
+ icon: CheckCheck,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "browser",
|
|
|
+ name: "Browser",
|
|
|
+ tooltipText: "Browser Settings",
|
|
|
+ headerText: "Browser Settings",
|
|
|
+ icon: SquareMousePointer,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "terminal",
|
|
|
+ name: "Terminal",
|
|
|
+ tooltipText: "Terminal Settings",
|
|
|
+ headerText: "Terminal Settings",
|
|
|
+ icon: SquareTerminal,
|
|
|
+ },
|
|
|
+ // Only show in dev mode
|
|
|
+ ...(IS_DEV
|
|
|
+ ? [
|
|
|
+ {
|
|
|
+ id: "debug",
|
|
|
+ name: "Debug",
|
|
|
+ tooltipText: "Debug Tools",
|
|
|
+ headerText: "Debug",
|
|
|
+ icon: FlaskConical,
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ : []),
|
|
|
+ {
|
|
|
+ id: "about",
|
|
|
+ name: "About",
|
|
|
+ tooltipText: "About Cline",
|
|
|
+ headerText: "About",
|
|
|
+ icon: Info,
|
|
|
+ },
|
|
|
+]
|
|
|
+
|
|
|
type SettingsViewProps = {
|
|
|
onDone: () => void
|
|
|
+ targetSection?: string
|
|
|
}
|
|
|
|
|
|
-const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|
|
+const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
|
|
|
const {
|
|
|
apiConfiguration,
|
|
|
version,
|
|
|
@@ -126,24 +221,35 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|
|
setPendingTabChange(null)
|
|
|
}
|
|
|
break
|
|
|
+ // Handle tab navigation through targetSection prop instead
|
|
|
case "grpc_response":
|
|
|
if (message.grpc_response?.message?.action === "scrollToSettings") {
|
|
|
- setTimeout(() => {
|
|
|
- const elementId = message.grpc_response?.message?.value
|
|
|
- if (elementId) {
|
|
|
- const element = document.getElementById(elementId)
|
|
|
- if (element) {
|
|
|
- element.scrollIntoView({ behavior: "smooth" })
|
|
|
-
|
|
|
- element.style.transition = "background-color 0.5s ease"
|
|
|
- element.style.backgroundColor = "var(--vscode-textPreformat-background)"
|
|
|
-
|
|
|
- setTimeout(() => {
|
|
|
- element.style.backgroundColor = "transparent"
|
|
|
- }, 1200)
|
|
|
- }
|
|
|
+ const tabId = message.grpc_response?.message?.value
|
|
|
+ if (tabId) {
|
|
|
+ console.log("Opening settings tab from GRPC response:", tabId)
|
|
|
+ // Check if the value corresponds to a valid tab ID
|
|
|
+ const isValidTabId = SETTINGS_TABS.some((tab) => tab.id === tabId)
|
|
|
+
|
|
|
+ if (isValidTabId) {
|
|
|
+ // Set the active tab directly
|
|
|
+ setActiveTab(tabId)
|
|
|
+ } else {
|
|
|
+ // Fall back to the old behavior of scrolling to an element
|
|
|
+ setTimeout(() => {
|
|
|
+ const element = document.getElementById(tabId)
|
|
|
+ if (element) {
|
|
|
+ element.scrollIntoView({ behavior: "smooth" })
|
|
|
+
|
|
|
+ element.style.transition = "background-color 0.5s ease"
|
|
|
+ element.style.backgroundColor = "var(--vscode-textPreformat-background)"
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ element.style.backgroundColor = "transparent"
|
|
|
+ }, 1200)
|
|
|
+ }
|
|
|
+ }, 300)
|
|
|
}
|
|
|
- }, 300)
|
|
|
+ }
|
|
|
}
|
|
|
break
|
|
|
}
|
|
|
@@ -161,7 +267,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- const handleTabChange = (tab: "plan" | "act") => {
|
|
|
+ const handlePlanActModeChange = (tab: "plan" | "act") => {
|
|
|
if (tab === chatSettings.mode) {
|
|
|
return
|
|
|
}
|
|
|
@@ -169,136 +275,325 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|
|
handleSubmit(true)
|
|
|
}
|
|
|
|
|
|
+ // Track active tab
|
|
|
+ const [activeTab, setActiveTab] = useState<string>(targetSection || SETTINGS_TABS[0].id)
|
|
|
+
|
|
|
+ // Update active tab when targetSection changes
|
|
|
+ useEffect(() => {
|
|
|
+ if (targetSection) {
|
|
|
+ setActiveTab(targetSection)
|
|
|
+ }
|
|
|
+ }, [targetSection])
|
|
|
+
|
|
|
+ // Enhanced tab change handler with debugging
|
|
|
+ const handleTabChange = useCallback(
|
|
|
+ (tabId: string) => {
|
|
|
+ console.log("Tab change requested:", tabId, "Current:", activeTab)
|
|
|
+ setActiveTab(tabId)
|
|
|
+ },
|
|
|
+ [activeTab],
|
|
|
+ )
|
|
|
+
|
|
|
+ // Debug tab changes
|
|
|
+ useEffect(() => {
|
|
|
+ console.log("Active tab changed to:", activeTab)
|
|
|
+ }, [activeTab])
|
|
|
+
|
|
|
+ // Track whether we're in compact mode
|
|
|
+ const [isCompactMode, setIsCompactMode] = useState(false)
|
|
|
+ const containerRef = useRef<HTMLDivElement>(null)
|
|
|
+
|
|
|
+ // Setup resize observer to detect when we should switch to compact mode
|
|
|
+ useEffect(() => {
|
|
|
+ if (!containerRef.current) return
|
|
|
+
|
|
|
+ const observer = new ResizeObserver((entries) => {
|
|
|
+ for (const entry of entries) {
|
|
|
+ // If container width is less than 500px, switch to compact mode
|
|
|
+ setIsCompactMode(entry.contentRect.width < 500)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ observer.observe(containerRef.current)
|
|
|
+
|
|
|
+ return () => {
|
|
|
+ observer?.disconnect()
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
+
|
|
|
return (
|
|
|
- <div className="fixed top-0 left-0 right-0 bottom-0 pt-[10px] pr-0 pb-0 pl-5 flex flex-col overflow-hidden">
|
|
|
- <div className="flex justify-between items-center mb-[13px] pr-[17px]">
|
|
|
- <h3 className="text-[var(--vscode-foreground)] m-0">Settings</h3>
|
|
|
- <VSCodeButton onClick={() => handleSubmit(false)}>Save</VSCodeButton>
|
|
|
- </div>
|
|
|
- <div className="grow overflow-y-scroll pr-2 flex flex-col">
|
|
|
- {/* Tabs container */}
|
|
|
- {planActSeparateModelsSetting ? (
|
|
|
- <div className="border border-solid border-[var(--vscode-panel-border)] rounded-md p-[10px] mb-5 bg-[var(--vscode-panel-background)]">
|
|
|
- <div className="flex gap-[1px] mb-[10px] -mt-2 border-0 border-b border-solid border-[var(--vscode-panel-border)]">
|
|
|
- <TabButton isActive={chatSettings.mode === "plan"} onClick={() => handleTabChange("plan")}>
|
|
|
- Plan Mode
|
|
|
- </TabButton>
|
|
|
- <TabButton isActive={chatSettings.mode === "act"} onClick={() => handleTabChange("act")}>
|
|
|
- Act Mode
|
|
|
- </TabButton>
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* Content container */}
|
|
|
- <div className="-mb-3">
|
|
|
- <ApiOptions
|
|
|
- key={chatSettings.mode}
|
|
|
- showModelOptions={true}
|
|
|
- apiErrorMessage={apiErrorMessage}
|
|
|
- modelIdErrorMessage={modelIdErrorMessage}
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- ) : (
|
|
|
- <ApiOptions
|
|
|
- key={"single"}
|
|
|
- showModelOptions={true}
|
|
|
- apiErrorMessage={apiErrorMessage}
|
|
|
- modelIdErrorMessage={modelIdErrorMessage}
|
|
|
- />
|
|
|
- )}
|
|
|
-
|
|
|
- <div className="mb-[5px]">
|
|
|
- <VSCodeTextArea
|
|
|
- value={customInstructions ?? ""}
|
|
|
- className="w-full"
|
|
|
- resize="vertical"
|
|
|
- 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 ?? "")}>
|
|
|
- <span className="font-medium">Custom Instructions</span>
|
|
|
- </VSCodeTextArea>
|
|
|
- <p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
|
|
|
- These instructions are added to the end of the system prompt sent with every request.
|
|
|
- </p>
|
|
|
+ <Tab>
|
|
|
+ <TabHeader className="flex justify-between items-center gap-2">
|
|
|
+ <div className="flex items-center gap-1">
|
|
|
+ <h3 className="text-[var(--vscode-foreground)] m-0">Settings</h3>
|
|
|
</div>
|
|
|
-
|
|
|
- {chatSettings && <PreferredLanguageSetting chatSettings={chatSettings} setChatSettings={setChatSettings} />}
|
|
|
-
|
|
|
- <div className="mb-[5px]">
|
|
|
- <VSCodeCheckbox
|
|
|
- className="mb-[5px]"
|
|
|
- checked={planActSeparateModelsSetting}
|
|
|
- onChange={(e: any) => {
|
|
|
- const checked = e.target.checked === true
|
|
|
- setPlanActSeparateModelsSetting(checked)
|
|
|
- }}>
|
|
|
- Use different models for Plan and Act modes
|
|
|
- </VSCodeCheckbox>
|
|
|
- <p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
|
|
|
- Switching between Plan and Act mode will persist the API and model used in the previous mode. This may be
|
|
|
- helpful e.g. when using a strong reasoning model to architect a plan for a cheaper coding model to act on.
|
|
|
- </p>
|
|
|
+ <div className="flex gap-2">
|
|
|
+ <VSCodeButton onClick={() => handleSubmit(false)}>Save</VSCodeButton>
|
|
|
</div>
|
|
|
+ </TabHeader>
|
|
|
|
|
|
- <div className="mb-[5px]">
|
|
|
- <VSCodeCheckbox
|
|
|
- className="mb-[5px]"
|
|
|
- checked={telemetrySetting === "enabled"}
|
|
|
- onChange={(e: any) => {
|
|
|
- const checked = e.target.checked === true
|
|
|
- setTelemetrySetting(checked ? "enabled" : "disabled")
|
|
|
- }}>
|
|
|
- Allow anonymous error and usage reporting
|
|
|
- </VSCodeCheckbox>
|
|
|
- <p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
|
|
|
- Help improve Cline by sending anonymous usage data and error reports. No code, prompts, or personal
|
|
|
- information are ever sent. See our{" "}
|
|
|
- <VSCodeLink href="https://docs.cline.bot/more-info/telemetry" className="text-inherit">
|
|
|
- telemetry overview
|
|
|
- </VSCodeLink>{" "}
|
|
|
- and{" "}
|
|
|
- <VSCodeLink href="https://cline.bot/privacy" className="text-inherit">
|
|
|
- privacy policy
|
|
|
- </VSCodeLink>{" "}
|
|
|
- for more details.
|
|
|
- </p>
|
|
|
- </div>
|
|
|
+ {/* Vertical tabs layout */}
|
|
|
+ <div ref={containerRef} className={cn(settingsTabsContainer, isCompactMode && "narrow")}>
|
|
|
+ {/* Tab sidebar */}
|
|
|
+ <TabList
|
|
|
+ value={activeTab}
|
|
|
+ onValueChange={handleTabChange}
|
|
|
+ className={cn(settingsTabList)}
|
|
|
+ data-compact={isCompactMode}>
|
|
|
+ {SETTINGS_TABS.map((tab) =>
|
|
|
+ isCompactMode ? (
|
|
|
+ <HeroTooltip key={tab.id} content={tab.tooltipText} placement="right">
|
|
|
+ <div
|
|
|
+ className={cn(
|
|
|
+ activeTab === tab.id
|
|
|
+ ? `${settingsTabTrigger} ${settingsTabTriggerActive}`
|
|
|
+ : settingsTabTrigger,
|
|
|
+ "focus:ring-0",
|
|
|
+ )}
|
|
|
+ data-compact={isCompactMode}
|
|
|
+ data-testid={`tab-${tab.id}`}
|
|
|
+ data-value={tab.id}
|
|
|
+ onClick={() => {
|
|
|
+ console.log("Compact tab clicked:", tab.id)
|
|
|
+ handleTabChange(tab.id)
|
|
|
+ }}>
|
|
|
+ <div className={cn("flex items-center gap-2", isCompactMode && "justify-center")}>
|
|
|
+ <tab.icon className="w-4 h-4" />
|
|
|
+ <span className="tab-label">{tab.name}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </HeroTooltip>
|
|
|
+ ) : (
|
|
|
+ <TabTrigger
|
|
|
+ key={tab.id}
|
|
|
+ value={tab.id}
|
|
|
+ className={cn(
|
|
|
+ activeTab === tab.id
|
|
|
+ ? `${settingsTabTrigger} ${settingsTabTriggerActive}`
|
|
|
+ : settingsTabTrigger,
|
|
|
+ "focus:ring-0",
|
|
|
+ )}
|
|
|
+ data-compact={isCompactMode}
|
|
|
+ data-testid={`tab-${tab.id}`}>
|
|
|
+ <div className={cn("flex items-center gap-2", isCompactMode && "justify-center")}>
|
|
|
+ <tab.icon className="w-4 h-4" />
|
|
|
+ <span className="tab-label">{tab.name}</span>
|
|
|
+ </div>
|
|
|
+ </TabTrigger>
|
|
|
+ ),
|
|
|
+ )}
|
|
|
+ </TabList>
|
|
|
|
|
|
- {/* Feature Settings Section */}
|
|
|
- <FeatureSettingsSection />
|
|
|
-
|
|
|
- {/* Browser Settings Section */}
|
|
|
- <BrowserSettingsSection />
|
|
|
-
|
|
|
- {/* Terminal Settings Section */}
|
|
|
- <TerminalSettingsSection />
|
|
|
-
|
|
|
- {IS_DEV && (
|
|
|
- <>
|
|
|
- <div className="mt-[10px] mb-1">Debug</div>
|
|
|
- <VSCodeButton
|
|
|
- onClick={handleResetState}
|
|
|
- className="mt-[5px] w-auto"
|
|
|
- style={{ backgroundColor: "var(--vscode-errorForeground)", color: "black" }}>
|
|
|
- Reset State
|
|
|
- </VSCodeButton>
|
|
|
- <p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
|
|
|
- This will reset all global state and secret storage in the extension.
|
|
|
- </p>
|
|
|
- </>
|
|
|
- )}
|
|
|
-
|
|
|
- <div className="text-center text-[var(--vscode-descriptionForeground)] text-xs leading-[1.2] px-0 py-0 pr-2 pb-[15px] mt-auto">
|
|
|
- <p className="break-words m-0 p-0">
|
|
|
- If you have any questions or feedback, feel free to open an issue at{" "}
|
|
|
- <VSCodeLink href="https://github.com/cline/cline" className="inline">
|
|
|
- https://github.com/cline/cline
|
|
|
- </VSCodeLink>
|
|
|
- </p>
|
|
|
- <p className="italic mt-[10px] mb-0 p-0">v{version}</p>
|
|
|
- </div>
|
|
|
+ {/* Helper function to render section header */}
|
|
|
+ {(() => {
|
|
|
+ const renderSectionHeader = (tabId: string) => {
|
|
|
+ const tab = SETTINGS_TABS.find((t) => t.id === tabId)
|
|
|
+ if (!tab) return null
|
|
|
+
|
|
|
+ return (
|
|
|
+ <SectionHeader>
|
|
|
+ <div className="flex items-center gap-2">
|
|
|
+ {(() => {
|
|
|
+ const Icon = tab.icon
|
|
|
+ return <Icon className="w-4" />
|
|
|
+ })()}
|
|
|
+ <div>{tab.headerText}</div>
|
|
|
+ </div>
|
|
|
+ </SectionHeader>
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <TabContent className="flex-1 overflow-auto">
|
|
|
+ {/* API Configuration Tab */}
|
|
|
+ {activeTab === "api-config" && (
|
|
|
+ <div>
|
|
|
+ {renderSectionHeader("api-config")}
|
|
|
+ <Section>
|
|
|
+ {/* Tabs container */}
|
|
|
+ {planActSeparateModelsSetting ? (
|
|
|
+ <div className="rounded-md mb-5 bg-[var(--vscode-panel-background)]">
|
|
|
+ <div className="flex gap-[1px] mb-[10px] -mt-2 border-0 border-b border-solid border-[var(--vscode-panel-border)]">
|
|
|
+ <TabButton
|
|
|
+ isActive={chatSettings.mode === "plan"}
|
|
|
+ onClick={() => handlePlanActModeChange("plan")}>
|
|
|
+ Plan Mode
|
|
|
+ </TabButton>
|
|
|
+ <TabButton
|
|
|
+ isActive={chatSettings.mode === "act"}
|
|
|
+ onClick={() => handlePlanActModeChange("act")}>
|
|
|
+ Act Mode
|
|
|
+ </TabButton>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* Content container */}
|
|
|
+ <div className="-mb-3">
|
|
|
+ <ApiOptions
|
|
|
+ key={chatSettings.mode}
|
|
|
+ showModelOptions={true}
|
|
|
+ apiErrorMessage={apiErrorMessage}
|
|
|
+ modelIdErrorMessage={modelIdErrorMessage}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <ApiOptions
|
|
|
+ key={"single"}
|
|
|
+ showModelOptions={true}
|
|
|
+ apiErrorMessage={apiErrorMessage}
|
|
|
+ modelIdErrorMessage={modelIdErrorMessage}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+
|
|
|
+ <div className="mb-[5px]">
|
|
|
+ <VSCodeCheckbox
|
|
|
+ className="mb-[5px]"
|
|
|
+ checked={planActSeparateModelsSetting}
|
|
|
+ onChange={(e: any) => {
|
|
|
+ const checked = e.target.checked === true
|
|
|
+ setPlanActSeparateModelsSetting(checked)
|
|
|
+ }}>
|
|
|
+ Use different models for Plan and Act modes
|
|
|
+ </VSCodeCheckbox>
|
|
|
+ <p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
|
|
|
+ Switching between Plan and Act mode will persist the API and model used in the
|
|
|
+ previous mode. This may be helpful e.g. when using a strong reasoning model to
|
|
|
+ architect a plan for a cheaper coding model to act on.
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="mb-[5px]">
|
|
|
+ <VSCodeTextArea
|
|
|
+ value={customInstructions ?? ""}
|
|
|
+ className="w-full"
|
|
|
+ resize="vertical"
|
|
|
+ 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 ?? "")}>
|
|
|
+ <span className="font-medium">Custom Instructions</span>
|
|
|
+ </VSCodeTextArea>
|
|
|
+ <p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
|
|
|
+ These instructions are added to the end of the system prompt sent with every
|
|
|
+ request.
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </Section>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* General Settings Tab */}
|
|
|
+ {activeTab === "general" && (
|
|
|
+ <div>
|
|
|
+ {renderSectionHeader("general")}
|
|
|
+ <Section>
|
|
|
+ {chatSettings && (
|
|
|
+ <PreferredLanguageSetting
|
|
|
+ chatSettings={chatSettings}
|
|
|
+ setChatSettings={setChatSettings}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+
|
|
|
+ <div className="mb-[5px]">
|
|
|
+ <VSCodeCheckbox
|
|
|
+ className="mb-[5px]"
|
|
|
+ checked={telemetrySetting === "enabled"}
|
|
|
+ onChange={(e: any) => {
|
|
|
+ const checked = e.target.checked === true
|
|
|
+ setTelemetrySetting(checked ? "enabled" : "disabled")
|
|
|
+ }}>
|
|
|
+ Allow anonymous error and usage reporting
|
|
|
+ </VSCodeCheckbox>
|
|
|
+ <p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
|
|
|
+ Help improve Cline by sending anonymous usage data and error reports. No code,
|
|
|
+ prompts, or personal information are ever sent. See our{" "}
|
|
|
+ <VSCodeLink
|
|
|
+ href="https://docs.cline.bot/more-info/telemetry"
|
|
|
+ className="text-inherit">
|
|
|
+ telemetry overview
|
|
|
+ </VSCodeLink>{" "}
|
|
|
+ and{" "}
|
|
|
+ <VSCodeLink href="https://cline.bot/privacy" className="text-inherit">
|
|
|
+ privacy policy
|
|
|
+ </VSCodeLink>{" "}
|
|
|
+ for more details.
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </Section>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Feature Settings Tab */}
|
|
|
+ {activeTab === "features" && (
|
|
|
+ <div>
|
|
|
+ {renderSectionHeader("features")}
|
|
|
+ <Section>
|
|
|
+ <FeatureSettingsSection />
|
|
|
+ </Section>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Browser Settings Tab */}
|
|
|
+ {activeTab === "browser" && (
|
|
|
+ <div>
|
|
|
+ {renderSectionHeader("browser")}
|
|
|
+ <Section>
|
|
|
+ <BrowserSettingsSection />
|
|
|
+ </Section>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Terminal Settings Tab */}
|
|
|
+ {activeTab === "terminal" && (
|
|
|
+ <div>
|
|
|
+ {renderSectionHeader("terminal")}
|
|
|
+ <Section>
|
|
|
+ <TerminalSettingsSection />
|
|
|
+ </Section>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Debug Tab (only in dev mode) */}
|
|
|
+ {IS_DEV && activeTab === "debug" && (
|
|
|
+ <div>
|
|
|
+ {renderSectionHeader("debug")}
|
|
|
+ <Section>
|
|
|
+ <VSCodeButton
|
|
|
+ onClick={handleResetState}
|
|
|
+ className="mt-[5px] w-auto"
|
|
|
+ style={{ backgroundColor: "var(--vscode-errorForeground)", color: "black" }}>
|
|
|
+ Reset State
|
|
|
+ </VSCodeButton>
|
|
|
+ <p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
|
|
|
+ This will reset all global state and secret storage in the extension.
|
|
|
+ </p>
|
|
|
+ </Section>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* About Tab */}
|
|
|
+ {activeTab === "about" && (
|
|
|
+ <div>
|
|
|
+ {renderSectionHeader("about")}
|
|
|
+ <Section>
|
|
|
+ <div className="text-center text-[var(--vscode-descriptionForeground)] text-xs leading-[1.2] px-0 py-0 pr-2 pb-[15px] mt-auto">
|
|
|
+ <p className="break-words m-0 p-0">
|
|
|
+ If you have any questions or feedback, feel free to open an issue at{" "}
|
|
|
+ <VSCodeLink href="https://github.com/cline/cline" className="inline">
|
|
|
+ https://github.com/cline/cline
|
|
|
+ </VSCodeLink>
|
|
|
+ </p>
|
|
|
+ <p className="italic mt-[10px] mb-0 p-0">v{version}</p>
|
|
|
+ </div>
|
|
|
+ </Section>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </TabContent>
|
|
|
+ )
|
|
|
+ })()}
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </Tab>
|
|
|
)
|
|
|
}
|
|
|
|