|
|
@@ -1,4 +1,14 @@
|
|
|
-import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"
|
|
|
+import React, {
|
|
|
+ forwardRef,
|
|
|
+ memo,
|
|
|
+ useCallback,
|
|
|
+ useEffect,
|
|
|
+ useImperativeHandle,
|
|
|
+ useLayoutEffect,
|
|
|
+ useMemo,
|
|
|
+ useRef,
|
|
|
+ useState,
|
|
|
+} from "react"
|
|
|
import { useAppTranslation } from "@/i18n/TranslationContext"
|
|
|
import {
|
|
|
CheckCheck,
|
|
|
@@ -14,7 +24,6 @@ import {
|
|
|
Info,
|
|
|
LucideIcon,
|
|
|
} from "lucide-react"
|
|
|
-import { CaretSortIcon } from "@radix-ui/react-icons"
|
|
|
|
|
|
import { ExperimentId } from "@roo/shared/experiments"
|
|
|
import { TelemetrySetting } from "@roo/shared/TelemetrySetting"
|
|
|
@@ -32,13 +41,13 @@ import {
|
|
|
AlertDialogHeader,
|
|
|
AlertDialogFooter,
|
|
|
Button,
|
|
|
- DropdownMenu,
|
|
|
- DropdownMenuTrigger,
|
|
|
- DropdownMenuContent,
|
|
|
- DropdownMenuItem,
|
|
|
+ Tooltip,
|
|
|
+ TooltipContent,
|
|
|
+ TooltipProvider,
|
|
|
+ TooltipTrigger,
|
|
|
} from "@/components/ui"
|
|
|
|
|
|
-import { Tab, TabContent, TabHeader } from "../common/Tab"
|
|
|
+import { Tab, TabContent, TabHeader, TabList, TabTrigger } from "../common/Tab"
|
|
|
import { SetCachedStateField, SetExperimentEnabled } from "./types"
|
|
|
import { SectionHeader } from "./SectionHeader"
|
|
|
import ApiConfigManager from "./ApiConfigManager"
|
|
|
@@ -53,6 +62,14 @@ import { ExperimentalSettings } from "./ExperimentalSettings"
|
|
|
import { LanguageSettings } from "./LanguageSettings"
|
|
|
import { About } from "./About"
|
|
|
import { Section } from "./Section"
|
|
|
+import { cn } from "@/lib/utils"
|
|
|
+
|
|
|
+export const settingsTabsContainer = "flex flex-1 overflow-hidden [&.narrow_.tab-label]:hidden"
|
|
|
+export const settingsTabList =
|
|
|
+ "w-48 data-[compact=true]:w-12 flex-shrink-0 flex flex-col overflow-y-auto overflow-x-hidden border-r border-vscode-sideBar-background"
|
|
|
+export 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-vscode-foreground opacity-70 hover:bg-vscode-list-hoverBackground data-[compact=true]:w-12 data-[compact=true]:p-4"
|
|
|
+export const settingsTabTriggerActive = "opacity-100 border-vscode-focusBorder bg-vscode-list-activeSelectionBackground"
|
|
|
|
|
|
export interface SettingsViewRef {
|
|
|
checkUnsaveChanges: (then: () => void) => void
|
|
|
@@ -87,6 +104,11 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
|
|
|
const [isDiscardDialogShow, setDiscardDialogShow] = useState(false)
|
|
|
const [isChangeDetected, setChangeDetected] = useState(false)
|
|
|
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
|
|
|
+ const [activeTab, setActiveTab] = useState<SectionName>(
|
|
|
+ targetSection && sectionNames.includes(targetSection as SectionName)
|
|
|
+ ? (targetSection as SectionName)
|
|
|
+ : "providers",
|
|
|
+ )
|
|
|
|
|
|
const prevApiConfigName = useRef(currentApiConfigName)
|
|
|
const confirmDialogHandler = useRef<() => void>()
|
|
|
@@ -125,7 +147,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
|
|
|
telemetrySetting,
|
|
|
terminalOutputLineLimit,
|
|
|
terminalShellIntegrationTimeout,
|
|
|
- terminalShellIntegrationDisabled,
|
|
|
+ terminalShellIntegrationDisabled, // Added from upstream
|
|
|
terminalCommandDelay,
|
|
|
terminalPowershellCounter,
|
|
|
terminalZshClearEolMark,
|
|
|
@@ -139,7 +161,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
|
|
|
terminalCompressProgressBar,
|
|
|
} = cachedState
|
|
|
|
|
|
- // Make sure apiConfiguration is initialized and managed by SettingsView.
|
|
|
const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration])
|
|
|
|
|
|
useEffect(() => {
|
|
|
@@ -279,81 +300,116 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
|
|
|
|
|
|
useImperativeHandle(ref, () => ({ checkUnsaveChanges }), [checkUnsaveChanges])
|
|
|
|
|
|
- const onConfirmDialogResult = useCallback((confirm: boolean) => {
|
|
|
- if (confirm) {
|
|
|
- confirmDialogHandler.current?.()
|
|
|
+ const onConfirmDialogResult = useCallback(
|
|
|
+ (confirm: boolean) => {
|
|
|
+ if (confirm) {
|
|
|
+ // Discard changes: Reset state and flag
|
|
|
+ setCachedState(extensionState) // Revert to original state
|
|
|
+ setChangeDetected(false) // Reset change flag
|
|
|
+ confirmDialogHandler.current?.() // Execute the pending action (e.g., tab switch)
|
|
|
+ }
|
|
|
+ // If confirm is false (Cancel), do nothing, dialog closes automatically
|
|
|
+ },
|
|
|
+ [extensionState], // Depend on extensionState to get the latest original state
|
|
|
+ )
|
|
|
+
|
|
|
+ // Handle tab changes with unsaved changes check
|
|
|
+ const handleTabChange = useCallback(
|
|
|
+ (newTab: SectionName) => {
|
|
|
+ // Directly switch tab without checking for unsaved changes
|
|
|
+ setActiveTab(newTab)
|
|
|
+ },
|
|
|
+ [], // No dependency on isChangeDetected needed anymore
|
|
|
+ )
|
|
|
+
|
|
|
+ // Store direct DOM element refs for each tab
|
|
|
+ const tabRefs = useRef<Record<SectionName, HTMLButtonElement | null>>(
|
|
|
+ Object.fromEntries(sectionNames.map((name) => [name, null])) as Record<SectionName, HTMLButtonElement | null>,
|
|
|
+ )
|
|
|
+
|
|
|
+ // 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()
|
|
|
}
|
|
|
}, [])
|
|
|
|
|
|
- const providersRef = useRef<HTMLDivElement>(null)
|
|
|
- const autoApproveRef = useRef<HTMLDivElement>(null)
|
|
|
- const browserRef = useRef<HTMLDivElement>(null)
|
|
|
- const checkpointsRef = useRef<HTMLDivElement>(null)
|
|
|
- const notificationsRef = useRef<HTMLDivElement>(null)
|
|
|
- const contextManagementRef = useRef<HTMLDivElement>(null)
|
|
|
- const terminalRef = useRef<HTMLDivElement>(null)
|
|
|
- const experimentalRef = useRef<HTMLDivElement>(null)
|
|
|
- const languageRef = useRef<HTMLDivElement>(null)
|
|
|
- const aboutRef = useRef<HTMLDivElement>(null)
|
|
|
-
|
|
|
- const sections: { id: SectionName; icon: LucideIcon; ref: React.RefObject<HTMLDivElement> }[] = useMemo(
|
|
|
+ const sections: { id: SectionName; icon: LucideIcon }[] = useMemo(
|
|
|
() => [
|
|
|
- { id: "providers", icon: Webhook, ref: providersRef },
|
|
|
- { id: "autoApprove", icon: CheckCheck, ref: autoApproveRef },
|
|
|
- { id: "browser", icon: SquareMousePointer, ref: browserRef },
|
|
|
- { id: "checkpoints", icon: GitBranch, ref: checkpointsRef },
|
|
|
- { id: "notifications", icon: Bell, ref: notificationsRef },
|
|
|
- { id: "contextManagement", icon: Database, ref: contextManagementRef },
|
|
|
- { id: "terminal", icon: SquareTerminal, ref: terminalRef },
|
|
|
- { id: "experimental", icon: FlaskConical, ref: experimentalRef },
|
|
|
- { id: "language", icon: Globe, ref: languageRef },
|
|
|
- { id: "about", icon: Info, ref: aboutRef },
|
|
|
- ],
|
|
|
- [
|
|
|
- providersRef,
|
|
|
- autoApproveRef,
|
|
|
- browserRef,
|
|
|
- checkpointsRef,
|
|
|
- notificationsRef,
|
|
|
- contextManagementRef,
|
|
|
- terminalRef,
|
|
|
- experimentalRef,
|
|
|
+ { id: "providers", icon: Webhook },
|
|
|
+ { id: "autoApprove", icon: CheckCheck },
|
|
|
+ { id: "browser", icon: SquareMousePointer },
|
|
|
+ { id: "checkpoints", icon: GitBranch },
|
|
|
+ { id: "notifications", icon: Bell },
|
|
|
+ { id: "contextManagement", icon: Database },
|
|
|
+ { id: "terminal", icon: SquareTerminal },
|
|
|
+ { id: "experimental", icon: FlaskConical },
|
|
|
+ { id: "language", icon: Globe },
|
|
|
+ { id: "about", icon: Info },
|
|
|
],
|
|
|
+ [], // No dependencies needed now
|
|
|
)
|
|
|
|
|
|
- const scrollToSection = (ref: React.RefObject<HTMLDivElement>) => ref.current?.scrollIntoView()
|
|
|
+ // Update target section logic to set active tab
|
|
|
+ useEffect(() => {
|
|
|
+ if (targetSection && sectionNames.includes(targetSection as SectionName)) {
|
|
|
+ setActiveTab(targetSection as SectionName)
|
|
|
+ }
|
|
|
+ }, [targetSection])
|
|
|
+
|
|
|
+ // Function to scroll the active tab into view for vertical layout
|
|
|
+ const scrollToActiveTab = useCallback(() => {
|
|
|
+ const activeTabElement = tabRefs.current[activeTab]
|
|
|
+
|
|
|
+ if (activeTabElement) {
|
|
|
+ activeTabElement.scrollIntoView({
|
|
|
+ behavior: "auto",
|
|
|
+ block: "nearest",
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }, [activeTab])
|
|
|
|
|
|
- // Scroll to target section when specified
|
|
|
+ // Effect to scroll when the active tab changes
|
|
|
useEffect(() => {
|
|
|
- if (targetSection) {
|
|
|
- const sectionObj = sections.find((section) => section.id === targetSection)
|
|
|
- if (sectionObj && sectionObj.ref.current) {
|
|
|
- // Use setTimeout to ensure the scroll happens after render
|
|
|
- setTimeout(() => scrollToSection(sectionObj.ref), 500)
|
|
|
+ scrollToActiveTab()
|
|
|
+ }, [activeTab, scrollToActiveTab])
|
|
|
+
|
|
|
+ // Effect to scroll when the webview becomes visible
|
|
|
+ useLayoutEffect(() => {
|
|
|
+ const handleMessage = (event: MessageEvent) => {
|
|
|
+ const message = event.data
|
|
|
+ if (message.type === "action" && message.action === "didBecomeVisible") {
|
|
|
+ scrollToActiveTab()
|
|
|
}
|
|
|
}
|
|
|
- }, [targetSection, sections])
|
|
|
+
|
|
|
+ window.addEventListener("message", handleMessage)
|
|
|
+
|
|
|
+ return () => {
|
|
|
+ window.removeEventListener("message", handleMessage)
|
|
|
+ }
|
|
|
+ }, [scrollToActiveTab])
|
|
|
|
|
|
return (
|
|
|
<Tab>
|
|
|
<TabHeader className="flex justify-between items-center gap-2">
|
|
|
<div className="flex items-center gap-1">
|
|
|
<h3 className="text-vscode-foreground m-0">{t("settings:header.title")}</h3>
|
|
|
- <DropdownMenu>
|
|
|
- <DropdownMenuTrigger asChild>
|
|
|
- <Button variant="ghost" size="icon" className="w-6 h-6">
|
|
|
- <CaretSortIcon />
|
|
|
- </Button>
|
|
|
- </DropdownMenuTrigger>
|
|
|
- <DropdownMenuContent align="start" side="bottom">
|
|
|
- {sections.map(({ id, icon: Icon, ref }) => (
|
|
|
- <DropdownMenuItem key={id} onClick={() => scrollToSection(ref)}>
|
|
|
- <Icon />
|
|
|
- <span>{t(`settings:sections.${id}`)}</span>
|
|
|
- </DropdownMenuItem>
|
|
|
- ))}
|
|
|
- </DropdownMenuContent>
|
|
|
- </DropdownMenu>
|
|
|
</div>
|
|
|
<div className="flex gap-2">
|
|
|
<Button
|
|
|
@@ -380,142 +436,214 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
|
|
|
</div>
|
|
|
</TabHeader>
|
|
|
|
|
|
- <TabContent className="p-0 divide-y divide-vscode-sideBar-background">
|
|
|
- <div ref={providersRef}>
|
|
|
- <SectionHeader>
|
|
|
- <div className="flex items-center gap-2">
|
|
|
- <Webhook className="w-4" />
|
|
|
- <div>{t("settings:sections.providers")}</div>
|
|
|
+ {/* Vertical tabs layout */}
|
|
|
+ <div ref={containerRef} className={cn(settingsTabsContainer, isCompactMode && "narrow")}>
|
|
|
+ {/* Tab sidebar */}
|
|
|
+ <TabList
|
|
|
+ value={activeTab}
|
|
|
+ onValueChange={(value) => handleTabChange(value as SectionName)}
|
|
|
+ className={cn(settingsTabList)}
|
|
|
+ data-compact={isCompactMode}
|
|
|
+ data-testid="settings-tab-list">
|
|
|
+ {sections.map(({ id, icon: Icon }) => {
|
|
|
+ const isSelected = id === activeTab
|
|
|
+ const onSelect = () => handleTabChange(id)
|
|
|
+
|
|
|
+ // Base TabTrigger component definition
|
|
|
+ // We pass isSelected manually for styling, but onSelect is handled conditionally
|
|
|
+ const triggerComponent = (
|
|
|
+ <TabTrigger
|
|
|
+ ref={(element) => (tabRefs.current[id] = element)}
|
|
|
+ value={id}
|
|
|
+ isSelected={isSelected} // Pass manually for styling state
|
|
|
+ className={cn(
|
|
|
+ isSelected // Use manual isSelected for styling
|
|
|
+ ? `${settingsTabTrigger} ${settingsTabTriggerActive}`
|
|
|
+ : settingsTabTrigger,
|
|
|
+ "focus:ring-0", // Remove the focus ring styling
|
|
|
+ )}
|
|
|
+ data-testid={`tab-${id}`}
|
|
|
+ data-compact={isCompactMode}>
|
|
|
+ <div className={cn("flex items-center gap-2", isCompactMode && "justify-center")}>
|
|
|
+ <Icon className="w-4 h-4" />
|
|
|
+ <span className="tab-label">{t(`settings:sections.${id}`)}</span>
|
|
|
+ </div>
|
|
|
+ </TabTrigger>
|
|
|
+ )
|
|
|
+
|
|
|
+ if (isCompactMode) {
|
|
|
+ // Wrap in Tooltip and manually add onClick to the trigger
|
|
|
+ return (
|
|
|
+ <TooltipProvider key={id} delayDuration={0}>
|
|
|
+ <Tooltip>
|
|
|
+ <TooltipTrigger asChild onClick={onSelect}>
|
|
|
+ {/* Clone to avoid ref issues if triggerComponent itself had a key */}
|
|
|
+ {React.cloneElement(triggerComponent)}
|
|
|
+ </TooltipTrigger>
|
|
|
+ <TooltipContent side="right" className="text-base">
|
|
|
+ <p className="m-0">{t(`settings:sections.${id}`)}</p>
|
|
|
+ </TooltipContent>
|
|
|
+ </Tooltip>
|
|
|
+ </TooltipProvider>
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ // Render trigger directly; TabList will inject onSelect via cloning
|
|
|
+ // Ensure the element passed to TabList has the key
|
|
|
+ return React.cloneElement(triggerComponent, { key: id })
|
|
|
+ }
|
|
|
+ })}
|
|
|
+ </TabList>
|
|
|
+
|
|
|
+ {/* Content area */}
|
|
|
+ <TabContent className="p-0 flex-1 overflow-auto">
|
|
|
+ {/* Providers Section */}
|
|
|
+ {activeTab === "providers" && (
|
|
|
+ <div>
|
|
|
+ <SectionHeader>
|
|
|
+ <div className="flex items-center gap-2">
|
|
|
+ <Webhook className="w-4" />
|
|
|
+ <div>{t("settings:sections.providers")}</div>
|
|
|
+ </div>
|
|
|
+ </SectionHeader>
|
|
|
+
|
|
|
+ <Section>
|
|
|
+ <ApiConfigManager
|
|
|
+ currentApiConfigName={currentApiConfigName}
|
|
|
+ listApiConfigMeta={listApiConfigMeta}
|
|
|
+ onSelectConfig={(configName: string) =>
|
|
|
+ checkUnsaveChanges(() =>
|
|
|
+ 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,
|
|
|
+ })
|
|
|
+ prevApiConfigName.current = newName
|
|
|
+ }}
|
|
|
+ onUpsertConfig={(configName: string) =>
|
|
|
+ vscode.postMessage({
|
|
|
+ type: "upsertApiConfiguration",
|
|
|
+ text: configName,
|
|
|
+ apiConfiguration,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ />
|
|
|
+ <ApiOptions
|
|
|
+ uriScheme={uriScheme}
|
|
|
+ apiConfiguration={apiConfiguration}
|
|
|
+ setApiConfigurationField={setApiConfigurationField}
|
|
|
+ errorMessage={errorMessage}
|
|
|
+ setErrorMessage={setErrorMessage}
|
|
|
+ />
|
|
|
+ </Section>
|
|
|
</div>
|
|
|
- </SectionHeader>
|
|
|
-
|
|
|
- <Section>
|
|
|
- <ApiConfigManager
|
|
|
- currentApiConfigName={currentApiConfigName}
|
|
|
- listApiConfigMeta={listApiConfigMeta}
|
|
|
- onSelectConfig={(configName: string) =>
|
|
|
- checkUnsaveChanges(() =>
|
|
|
- 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,
|
|
|
- })
|
|
|
- prevApiConfigName.current = newName
|
|
|
- }}
|
|
|
- onUpsertConfig={(configName: string) =>
|
|
|
- vscode.postMessage({
|
|
|
- type: "upsertApiConfiguration",
|
|
|
- text: configName,
|
|
|
- apiConfiguration,
|
|
|
- })
|
|
|
- }
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Auto-Approve Section */}
|
|
|
+ {activeTab === "autoApprove" && (
|
|
|
+ <AutoApproveSettings
|
|
|
+ alwaysAllowReadOnly={alwaysAllowReadOnly}
|
|
|
+ alwaysAllowReadOnlyOutsideWorkspace={alwaysAllowReadOnlyOutsideWorkspace}
|
|
|
+ alwaysAllowWrite={alwaysAllowWrite}
|
|
|
+ alwaysAllowWriteOutsideWorkspace={alwaysAllowWriteOutsideWorkspace}
|
|
|
+ writeDelayMs={writeDelayMs}
|
|
|
+ alwaysAllowBrowser={alwaysAllowBrowser}
|
|
|
+ alwaysApproveResubmit={alwaysApproveResubmit}
|
|
|
+ requestDelaySeconds={requestDelaySeconds}
|
|
|
+ alwaysAllowMcp={alwaysAllowMcp}
|
|
|
+ alwaysAllowModeSwitch={alwaysAllowModeSwitch}
|
|
|
+ alwaysAllowSubtasks={alwaysAllowSubtasks}
|
|
|
+ alwaysAllowExecute={alwaysAllowExecute}
|
|
|
+ allowedCommands={allowedCommands}
|
|
|
+ setCachedStateField={setCachedStateField}
|
|
|
/>
|
|
|
- <ApiOptions
|
|
|
- uriScheme={uriScheme}
|
|
|
- apiConfiguration={apiConfiguration}
|
|
|
- setApiConfigurationField={setApiConfigurationField}
|
|
|
- errorMessage={errorMessage}
|
|
|
- setErrorMessage={setErrorMessage}
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Browser Section */}
|
|
|
+ {activeTab === "browser" && (
|
|
|
+ <BrowserSettings
|
|
|
+ browserToolEnabled={browserToolEnabled}
|
|
|
+ browserViewportSize={browserViewportSize}
|
|
|
+ screenshotQuality={screenshotQuality}
|
|
|
+ remoteBrowserHost={remoteBrowserHost}
|
|
|
+ remoteBrowserEnabled={remoteBrowserEnabled}
|
|
|
+ setCachedStateField={setCachedStateField}
|
|
|
/>
|
|
|
- </Section>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div ref={autoApproveRef}>
|
|
|
- <AutoApproveSettings
|
|
|
- alwaysAllowReadOnly={alwaysAllowReadOnly}
|
|
|
- alwaysAllowReadOnlyOutsideWorkspace={alwaysAllowReadOnlyOutsideWorkspace}
|
|
|
- alwaysAllowWrite={alwaysAllowWrite}
|
|
|
- alwaysAllowWriteOutsideWorkspace={alwaysAllowWriteOutsideWorkspace}
|
|
|
- writeDelayMs={writeDelayMs}
|
|
|
- alwaysAllowBrowser={alwaysAllowBrowser}
|
|
|
- alwaysApproveResubmit={alwaysApproveResubmit}
|
|
|
- requestDelaySeconds={requestDelaySeconds}
|
|
|
- alwaysAllowMcp={alwaysAllowMcp}
|
|
|
- alwaysAllowModeSwitch={alwaysAllowModeSwitch}
|
|
|
- alwaysAllowSubtasks={alwaysAllowSubtasks}
|
|
|
- alwaysAllowExecute={alwaysAllowExecute}
|
|
|
- allowedCommands={allowedCommands}
|
|
|
- setCachedStateField={setCachedStateField}
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div ref={browserRef}>
|
|
|
- <BrowserSettings
|
|
|
- browserToolEnabled={browserToolEnabled}
|
|
|
- browserViewportSize={browserViewportSize}
|
|
|
- screenshotQuality={screenshotQuality}
|
|
|
- remoteBrowserHost={remoteBrowserHost}
|
|
|
- remoteBrowserEnabled={remoteBrowserEnabled}
|
|
|
- setCachedStateField={setCachedStateField}
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div ref={checkpointsRef}>
|
|
|
- <CheckpointSettings
|
|
|
- enableCheckpoints={enableCheckpoints}
|
|
|
- setCachedStateField={setCachedStateField}
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div ref={notificationsRef}>
|
|
|
- <NotificationSettings
|
|
|
- ttsEnabled={ttsEnabled}
|
|
|
- ttsSpeed={ttsSpeed}
|
|
|
- soundEnabled={soundEnabled}
|
|
|
- soundVolume={soundVolume}
|
|
|
- setCachedStateField={setCachedStateField}
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div ref={contextManagementRef}>
|
|
|
- <ContextManagementSettings
|
|
|
- maxOpenTabsContext={maxOpenTabsContext}
|
|
|
- maxWorkspaceFiles={maxWorkspaceFiles ?? 200}
|
|
|
- showRooIgnoredFiles={showRooIgnoredFiles}
|
|
|
- maxReadFileLine={maxReadFileLine}
|
|
|
- setCachedStateField={setCachedStateField}
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div ref={terminalRef}>
|
|
|
- <TerminalSettings
|
|
|
- terminalOutputLineLimit={terminalOutputLineLimit}
|
|
|
- terminalShellIntegrationTimeout={terminalShellIntegrationTimeout}
|
|
|
- terminalShellIntegrationDisabled={terminalShellIntegrationDisabled}
|
|
|
- terminalCommandDelay={terminalCommandDelay}
|
|
|
- terminalPowershellCounter={terminalPowershellCounter}
|
|
|
- terminalZshClearEolMark={terminalZshClearEolMark}
|
|
|
- terminalZshOhMy={terminalZshOhMy}
|
|
|
- terminalZshP10k={terminalZshP10k}
|
|
|
- terminalZdotdir={terminalZdotdir}
|
|
|
- terminalCompressProgressBar={terminalCompressProgressBar}
|
|
|
- setCachedStateField={setCachedStateField}
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div ref={experimentalRef}>
|
|
|
- <ExperimentalSettings setExperimentEnabled={setExperimentEnabled} experiments={experiments} />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div ref={languageRef}>
|
|
|
- <LanguageSettings language={language || "en"} setCachedStateField={setCachedStateField} />
|
|
|
- </div>
|
|
|
+ )}
|
|
|
|
|
|
- <div ref={aboutRef}>
|
|
|
- <About
|
|
|
- version={version}
|
|
|
- telemetrySetting={telemetrySetting}
|
|
|
- setTelemetrySetting={setTelemetrySetting}
|
|
|
- />
|
|
|
- </div>
|
|
|
- </TabContent>
|
|
|
+ {/* Checkpoints Section */}
|
|
|
+ {activeTab === "checkpoints" && (
|
|
|
+ <CheckpointSettings
|
|
|
+ enableCheckpoints={enableCheckpoints}
|
|
|
+ setCachedStateField={setCachedStateField}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Notifications Section */}
|
|
|
+ {activeTab === "notifications" && (
|
|
|
+ <NotificationSettings
|
|
|
+ ttsEnabled={ttsEnabled}
|
|
|
+ ttsSpeed={ttsSpeed}
|
|
|
+ soundEnabled={soundEnabled}
|
|
|
+ soundVolume={soundVolume}
|
|
|
+ setCachedStateField={setCachedStateField}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Context Management Section */}
|
|
|
+ {activeTab === "contextManagement" && (
|
|
|
+ <ContextManagementSettings
|
|
|
+ maxOpenTabsContext={maxOpenTabsContext}
|
|
|
+ maxWorkspaceFiles={maxWorkspaceFiles ?? 200}
|
|
|
+ showRooIgnoredFiles={showRooIgnoredFiles}
|
|
|
+ maxReadFileLine={maxReadFileLine}
|
|
|
+ setCachedStateField={setCachedStateField}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Terminal Section */}
|
|
|
+ {activeTab === "terminal" && (
|
|
|
+ <TerminalSettings
|
|
|
+ terminalOutputLineLimit={terminalOutputLineLimit}
|
|
|
+ terminalShellIntegrationTimeout={terminalShellIntegrationTimeout}
|
|
|
+ terminalShellIntegrationDisabled={terminalShellIntegrationDisabled}
|
|
|
+ terminalCommandDelay={terminalCommandDelay}
|
|
|
+ terminalPowershellCounter={terminalPowershellCounter}
|
|
|
+ terminalZshClearEolMark={terminalZshClearEolMark}
|
|
|
+ terminalZshOhMy={terminalZshOhMy}
|
|
|
+ terminalZshP10k={terminalZshP10k}
|
|
|
+ terminalZdotdir={terminalZdotdir}
|
|
|
+ terminalCompressProgressBar={terminalCompressProgressBar}
|
|
|
+ setCachedStateField={setCachedStateField}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Experimental Section */}
|
|
|
+ {activeTab === "experimental" && (
|
|
|
+ <ExperimentalSettings setExperimentEnabled={setExperimentEnabled} experiments={experiments} />
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Language Section */}
|
|
|
+ {activeTab === "language" && (
|
|
|
+ <LanguageSettings language={language || "en"} setCachedStateField={setCachedStateField} />
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* About Section */}
|
|
|
+ {activeTab === "about" && (
|
|
|
+ <About
|
|
|
+ version={version}
|
|
|
+ telemetrySetting={telemetrySetting}
|
|
|
+ setTelemetrySetting={setTelemetrySetting}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </TabContent>
|
|
|
+ </div>
|
|
|
|
|
|
<AlertDialog open={isDiscardDialogShow} onOpenChange={setDiscardDialogShow}>
|
|
|
<AlertDialogContent>
|