diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 4a934e9fa0e..e96f16103a7 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1509,12 +1509,12 @@ export class ClineProvider condensingApiConfigId, customCondensingPrompt, codebaseIndexModels: codebaseIndexModels ?? EMBEDDING_MODEL_PROFILES, - codebaseIndexConfig: codebaseIndexConfig ?? { - codebaseIndexEnabled: true, - codebaseIndexQdrantUrl: "http://localhost:6333", - codebaseIndexEmbedderProvider: "openai", - codebaseIndexEmbedderBaseUrl: "", - codebaseIndexEmbedderModelId: "", + codebaseIndexConfig: { + codebaseIndexEnabled: codebaseIndexConfig?.codebaseIndexEnabled ?? true, + codebaseIndexQdrantUrl: codebaseIndexConfig?.codebaseIndexQdrantUrl ?? "http://localhost:6333", + codebaseIndexEmbedderProvider: codebaseIndexConfig?.codebaseIndexEmbedderProvider ?? "openai", + codebaseIndexEmbedderBaseUrl: codebaseIndexConfig?.codebaseIndexEmbedderBaseUrl ?? "", + codebaseIndexEmbedderModelId: codebaseIndexConfig?.codebaseIndexEmbedderModelId ?? "", }, mdmCompliant: this.checkMdmCompliance(), profileThresholds: profileThresholds ?? {}, @@ -1667,12 +1667,14 @@ export class ClineProvider condensingApiConfigId: stateValues.condensingApiConfigId, customCondensingPrompt: stateValues.customCondensingPrompt, codebaseIndexModels: stateValues.codebaseIndexModels ?? EMBEDDING_MODEL_PROFILES, - codebaseIndexConfig: stateValues.codebaseIndexConfig ?? { - codebaseIndexEnabled: true, - codebaseIndexQdrantUrl: "http://localhost:6333", - codebaseIndexEmbedderProvider: "openai", - codebaseIndexEmbedderBaseUrl: "", - codebaseIndexEmbedderModelId: "", + codebaseIndexConfig: { + codebaseIndexEnabled: stateValues.codebaseIndexConfig?.codebaseIndexEnabled ?? true, + codebaseIndexQdrantUrl: + stateValues.codebaseIndexConfig?.codebaseIndexQdrantUrl ?? "http://localhost:6333", + codebaseIndexEmbedderProvider: + stateValues.codebaseIndexConfig?.codebaseIndexEmbedderProvider ?? "openai", + codebaseIndexEmbedderBaseUrl: stateValues.codebaseIndexConfig?.codebaseIndexEmbedderBaseUrl ?? "", + codebaseIndexEmbedderModelId: stateValues.codebaseIndexConfig?.codebaseIndexEmbedderModelId ?? "", }, profileThresholds: stateValues.profileThresholds ?? {}, } diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index a6577fb2fbf..fe5033d29e9 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1936,9 +1936,10 @@ export const webviewMessageHandler = async ( const embedderProviderChanged = currentConfig.codebaseIndexEmbedderProvider !== settings.codebaseIndexEmbedderProvider - // Save global state settings atomically (without codebaseIndexEnabled which is now in global settings) + // Save global state settings atomically const globalStateConfig = { ...currentConfig, + codebaseIndexEnabled: settings.codebaseIndexEnabled, codebaseIndexQdrantUrl: settings.codebaseIndexQdrantUrl, codebaseIndexEmbedderProvider: settings.codebaseIndexEmbedderProvider, codebaseIndexEmbedderBaseUrl: settings.codebaseIndexEmbedderBaseUrl, diff --git a/src/services/code-index/__tests__/config-manager.spec.ts b/src/services/code-index/__tests__/config-manager.spec.ts index 641abfa306a..6d0e59e827a 100644 --- a/src/services/code-index/__tests__/config-manager.spec.ts +++ b/src/services/code-index/__tests__/config-manager.spec.ts @@ -1,15 +1,27 @@ +// npx vitest services/code-index/__tests__/config-manager.spec.ts + +import { describe, it, expect, beforeEach, vi } from "vitest" import { CodeIndexConfigManager } from "../config-manager" +import { ContextProxy } from "../../../core/config/ContextProxy" +import { PreviousConfigSnapshot } from "../interfaces/config" + +// Mock ContextProxy +vi.mock("../../../core/config/ContextProxy") describe("CodeIndexConfigManager", () => { let mockContextProxy: any let configManager: CodeIndexConfigManager beforeEach(() => { + // Reset mocks + vi.clearAllMocks() + // Setup mock ContextProxy mockContextProxy = { - getGlobalState: vitest.fn(), - getSecret: vitest.fn().mockReturnValue(undefined), - refreshSecrets: vitest.fn().mockResolvedValue(undefined), + getGlobalState: vi.fn(), + getSecret: vi.fn().mockReturnValue(undefined), + refreshSecrets: vi.fn().mockResolvedValue(undefined), + updateGlobalState: vi.fn(), } configManager = new CodeIndexConfigManager(mockContextProxy) @@ -37,6 +49,39 @@ describe("CodeIndexConfigManager", () => { }) }) + describe("isFeatureEnabled", () => { + it("should return false when codebaseIndexEnabled is false", async () => { + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + + // Re-create instance to load the configuration + configManager = new CodeIndexConfigManager(mockContextProxy) + expect(configManager.isFeatureEnabled).toBe(false) + }) + + it("should return true when codebaseIndexEnabled is true", async () => { + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + + // Re-create instance to load the configuration + configManager = new CodeIndexConfigManager(mockContextProxy) + expect(configManager.isFeatureEnabled).toBe(true) + }) + + it("should default to true when codebaseIndexEnabled is not set", async () => { + mockContextProxy.getGlobalState.mockReturnValue({}) + mockContextProxy.getSecret.mockReturnValue(undefined) + + // Re-create instance to load the configuration + configManager = new CodeIndexConfigManager(mockContextProxy) + expect(configManager.isFeatureEnabled).toBe(true) + }) + }) + describe("loadConfiguration", () => { it("should load default configuration when no state exists", async () => { mockContextProxy.getGlobalState.mockReturnValue(undefined) @@ -1300,4 +1345,294 @@ describe("CodeIndexConfigManager", () => { expect(result.requiresRestart).toBe(false) }) }) + + describe("doesConfigChangeRequireRestart", () => { + it("should return true when enabling the feature", async () => { + // Initial state: disabled + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + configManager = new CodeIndexConfigManager(mockContextProxy) + + // Get the initial snapshot + const { configSnapshot: previousSnapshot } = await configManager.loadConfiguration() + + // Update the internal state to enabled with proper configuration + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + // Load the new configuration - this will internally call doesConfigChangeRequireRestart + const { requiresRestart } = await configManager.loadConfiguration() + + expect(requiresRestart).toBe(true) + }) + + it("should return true when disabling the feature", async () => { + // Initial state: enabled and configured + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + configManager = new CodeIndexConfigManager(mockContextProxy) + + const previousSnapshot: PreviousConfigSnapshot = { + enabled: true, + configured: true, + embedderProvider: "openai", + openAiKey: "test-key", + qdrantUrl: "http://localhost:6333", + } + + // Update to disabled + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + await configManager.loadConfiguration() + + const result = configManager.doesConfigChangeRequireRestart(previousSnapshot) + expect(result).toBe(true) + }) + + it("should return false when enabled state does not change (both enabled)", async () => { + // Initial state: enabled and configured + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + configManager = new CodeIndexConfigManager(mockContextProxy) + + // Get initial configuration + const { configSnapshot: previousSnapshot } = await configManager.loadConfiguration() + + // Load again with same config - should not require restart + const { requiresRestart } = await configManager.loadConfiguration() + + expect(requiresRestart).toBe(false) + }) + + it("should return false when enabled state does not change (both disabled)", async () => { + // Initial state: disabled + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + configManager = new CodeIndexConfigManager(mockContextProxy) + + const previousSnapshot: PreviousConfigSnapshot = { + enabled: false, + configured: false, + embedderProvider: "openai", + } + + // Same config, still disabled + const result = configManager.doesConfigChangeRequireRestart(previousSnapshot) + expect(result).toBe(false) + }) + + it("should return true when provider changes while enabled", async () => { + // Initial state: enabled with openai + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "ollama", + codebaseIndexOllamaBaseUrl: "http://localhost:11434", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + configManager = new CodeIndexConfigManager(mockContextProxy) + + const previousSnapshot: PreviousConfigSnapshot = { + enabled: true, + configured: true, + embedderProvider: "openai", + openAiKey: "test-key", + qdrantUrl: "http://localhost:6333", + } + + const result = configManager.doesConfigChangeRequireRestart(previousSnapshot) + expect(result).toBe(true) + }) + + it("should return false when provider changes while disabled", async () => { + // Initial state: disabled with openai + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, + codebaseIndexEmbedderProvider: "ollama", + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + configManager = new CodeIndexConfigManager(mockContextProxy) + + const previousSnapshot: PreviousConfigSnapshot = { + enabled: false, + configured: false, + embedderProvider: "openai", + } + + // Provider changed but feature is disabled + const result = configManager.doesConfigChangeRequireRestart(previousSnapshot) + expect(result).toBe(false) + }) + }) + + describe("loadConfiguration", () => { + it("should load configuration and return proper structure", async () => { + const mockConfigValues = { + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-ada-002", + codebaseIndexQdrantUrl: "http://localhost:6333", + codebaseIndexSearchMinScore: 0.5, + codebaseIndexSearchMaxResults: 20, + } + + mockContextProxy.getGlobalState.mockReturnValue(mockConfigValues) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + if (key === "codeIndexQdrantApiKey") return "qdrant-key" + return undefined + }) + + const result = await configManager.loadConfiguration() + + // Verify the structure + expect(result).toHaveProperty("configSnapshot") + expect(result).toHaveProperty("currentConfig") + expect(result).toHaveProperty("requiresRestart") + + // Verify current config reflects loaded values + expect(result.currentConfig.embedderProvider).toBe("openai") + expect(result.currentConfig.isConfigured).toBe(true) + }) + + it("should detect restart requirement when configuration changes", async () => { + // Initial state: disabled + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + configManager = new CodeIndexConfigManager(mockContextProxy) + + // Get initial state + await configManager.loadConfiguration() + + // Change to enabled with proper configuration + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(true) + }) + }) + + describe("getConfig", () => { + it("should return the current configuration", () => { + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + configManager = new CodeIndexConfigManager(mockContextProxy) + const config = configManager.getConfig() + + expect(config).toHaveProperty("isConfigured") + expect(config).toHaveProperty("embedderProvider") + expect(config.embedderProvider).toBe("openai") + }) + }) + + describe("isConfigured", () => { + it("should return true when OpenAI provider is properly configured", () => { + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + configManager = new CodeIndexConfigManager(mockContextProxy) + expect(configManager.isConfigured()).toBe(true) + }) + + it("should return false when OpenAI provider is missing API key", () => { + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "openai", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + + configManager = new CodeIndexConfigManager(mockContextProxy) + expect(configManager.isConfigured()).toBe(false) + }) + + it("should return true when Ollama provider is properly configured", () => { + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "ollama", + codebaseIndexEmbedderBaseUrl: "http://localhost:11434", + codebaseIndexQdrantUrl: "http://localhost:6333", + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + + configManager = new CodeIndexConfigManager(mockContextProxy) + expect(configManager.isConfigured()).toBe(true) + }) + + it("should return false when Qdrant URL is missing", () => { + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexEmbedderProvider: "openai", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + configManager = new CodeIndexConfigManager(mockContextProxy) + expect(configManager.isConfigured()).toBe(false) + }) + }) }) diff --git a/src/services/code-index/config-manager.ts b/src/services/code-index/config-manager.ts index 245621a1bd9..f022aec7806 100644 --- a/src/services/code-index/config-manager.ts +++ b/src/services/code-index/config-manager.ts @@ -10,6 +10,7 @@ import { getDefaultModelId, getModelDimension, getModelScoreThreshold } from ".. * Handles loading, validating, and providing access to configuration values. */ export class CodeIndexConfigManager { + private codebaseIndexEnabled: boolean = true private embedderProvider: EmbedderProvider = "openai" private modelId?: string private modelDimension?: number @@ -68,7 +69,7 @@ export class CodeIndexConfigManager { const geminiApiKey = this.contextProxy?.getSecret("codebaseIndexGeminiApiKey") ?? "" // Update instance variables with configuration - // Note: codebaseIndexEnabled is no longer used as the feature is always enabled + this.codebaseIndexEnabled = codebaseIndexEnabled ?? true this.qdrantUrl = codebaseIndexQdrantUrl this.qdrantApiKey = qdrantApiKey ?? "" this.searchMinScore = codebaseIndexSearchMinScore @@ -142,7 +143,7 @@ export class CodeIndexConfigManager { }> { // Capture the ACTUAL previous state before loading new configuration const previousConfigSnapshot: PreviousConfigSnapshot = { - enabled: true, // Feature is always enabled + enabled: this.codebaseIndexEnabled, configured: this.isConfigured(), embedderProvider: this.embedderProvider, modelId: this.modelId, @@ -243,19 +244,26 @@ export class CodeIndexConfigManager { const prevQdrantUrl = prev?.qdrantUrl ?? "" const prevQdrantApiKey = prev?.qdrantApiKey ?? "" - // 1. Transition from unconfigured to configured - // Since the feature is always enabled, we only check configuration status - if (!prevConfigured && nowConfigured) { + // 1. Transition from disabled/unconfigured to enabled/configured + if ((!prevEnabled || !prevConfigured) && this.codebaseIndexEnabled && nowConfigured) { + return true + } + + // 2. Transition from enabled to disabled + if (prevEnabled && !this.codebaseIndexEnabled) { return true } // 3. If wasn't ready before and isn't ready now, no restart needed - if (!prevConfigured && !nowConfigured) { + if ((!prevEnabled || !prevConfigured) && (!this.codebaseIndexEnabled || !nowConfigured)) { return false } // 4. CRITICAL CHANGES - Always restart for these - // Since feature is always enabled, we always check for critical changes + // Only check for critical changes if feature is enabled + if (!this.codebaseIndexEnabled) { + return false + } // Provider change if (prevProvider !== this.embedderProvider) { @@ -354,7 +362,7 @@ export class CodeIndexConfigManager { * Gets whether the code indexing feature is enabled */ public get isFeatureEnabled(): boolean { - return true + return this.codebaseIndexEnabled } /** diff --git a/src/services/code-index/manager.ts b/src/services/code-index/manager.ts index 6fa86b4e4fb..8a330e7952c 100644 --- a/src/services/code-index/manager.ts +++ b/src/services/code-index/manager.ts @@ -303,8 +303,25 @@ export class CodeIndexManager { const isFeatureEnabled = this.isFeatureEnabled const isFeatureConfigured = this.isFeatureConfigured + // If feature is disabled, stop the service + if (!isFeatureEnabled) { + // Stop the orchestrator if it exists + if (this._orchestrator) { + this._orchestrator.stopWatcher() + } + // Set state to indicate service is disabled + this._stateManager.setSystemState("Standby", "Code indexing is disabled") + return + } + if (requiresRestart && isFeatureEnabled && isFeatureConfigured) { try { + // Ensure cacheManager is initialized before recreating services + if (!this._cacheManager) { + this._cacheManager = new CacheManager(this.context, this.workspacePath) + await this._cacheManager.initialize() + } + // Recreate services with new configuration await this._recreateServices() } catch (error) { diff --git a/webview-ui/src/components/chat/CodeIndexPopover.tsx b/webview-ui/src/components/chat/CodeIndexPopover.tsx index 1243b7efa2c..84703bcae26 100644 --- a/webview-ui/src/components/chat/CodeIndexPopover.tsx +++ b/webview-ui/src/components/chat/CodeIndexPopover.tsx @@ -7,6 +7,7 @@ import { VSCodeDropdown, VSCodeOption, VSCodeLink, + VSCodeCheckbox, } from "@vscode/webview-ui-toolkit/react" import * as ProgressPrimitive from "@radix-ui/react-progress" import { vscode } from "@src/utils/vscode" @@ -72,6 +73,7 @@ interface LocalCodeIndexSettings { // Validation schema for codebase index settings const createValidationSchema = (provider: EmbedderProvider, t: any) => { const baseSchema = z.object({ + codebaseIndexEnabled: z.boolean(), codebaseIndexQdrantUrl: z .string() .min(1, t("settings:codeIndex.validation.qdrantUrlRequired")) @@ -213,6 +215,10 @@ export const CodeIndexPopover: React.FC = ({ } }, [open]) + // Use a ref to capture current settings for the save handler + const currentSettingsRef = useRef(currentSettings) + currentSettingsRef.current = currentSettings + // Listen for indexing status updates and save responses useEffect(() => { const handleMessage = (event: MessageEvent) => { @@ -227,21 +233,24 @@ export const CodeIndexPopover: React.FC = ({ } else if (event.data.type === "codeIndexSettingsSaved") { if (event.data.success) { setSaveStatus("saved") - // Don't update initial settings here - wait for the secret status response - // Request updated secret status after save + // Update initial settings to match current settings after successful save + // This ensures hasUnsavedChanges becomes false + const savedSettings = { ...currentSettingsRef.current } + setInitialSettings(savedSettings) + // Also update current settings to maintain consistency + setCurrentSettings(savedSettings) + // Request secret status to ensure we have the latest state + // This is important to maintain placeholder display after save + vscode.postMessage({ type: "requestCodeIndexSecretStatus" }) - // Reset status after 3 seconds - setTimeout(() => { - setSaveStatus("idle") - }, 3000) + + setSaveStatus("idle") } else { setSaveStatus("error") setSaveError(event.data.error || t("settings:codeIndex.saveError")) // Clear error message after 5 seconds - setTimeout(() => { - setSaveStatus("idle") - setSaveError(null) - }, 5000) + setSaveStatus("idle") + setSaveError(null) } } } @@ -284,14 +293,18 @@ export const CodeIndexPopover: React.FC = ({ return updated } - setCurrentSettings(updateWithSecrets) - setInitialSettings(updateWithSecrets) + // Only update settings if we're not in the middle of saving + // After save is complete (saved status), we still want to update to maintain consistency + if (saveStatus === "idle" || saveStatus === "saved") { + setCurrentSettings(updateWithSecrets) + setInitialSettings(updateWithSecrets) + } } } window.addEventListener("message", handleMessage) return () => window.removeEventListener("message", handleMessage) - }, []) + }, [saveStatus]) // Generic comparison function that detects changes between initial and current settings const hasUnsavedChanges = useMemo(() => { @@ -417,20 +430,26 @@ export const CodeIndexPopover: React.FC = ({ setSaveStatus("saving") setSaveError(null) - // Prepare settings to save - include all fields except secrets with placeholder values + // Prepare settings to save const settingsToSave: any = {} // Iterate through all current settings for (const [key, value] of Object.entries(currentSettings)) { - // Skip secret fields that still have placeholder value + // For secret fields with placeholder, don't send the placeholder + // but also don't send an empty string - just skip the field + // This tells the backend to keep the existing secret if (value === SECRET_PLACEHOLDER) { + // Skip sending placeholder values - backend will preserve existing secrets continue } - // Include all other fields + // Include all other fields, including empty strings (which clear secrets) settingsToSave[key] = value } + // Always include codebaseIndexEnabled to ensure it's persisted + settingsToSave.codebaseIndexEnabled = currentSettings.codebaseIndexEnabled + // Save settings to backend vscode.postMessage({ type: "saveCodeIndexSettingsAtomic", @@ -494,6 +513,20 @@ export const CodeIndexPopover: React.FC = ({
+ {/* Enable/Disable Toggle */} +
+
+ updateSetting("codebaseIndexEnabled", e.target.checked)}> + {t("settings:codeIndex.enableLabel")} + + + + +
+
+ {/* Status Section */}

{t("settings:codeIndex.statusTitle")}

@@ -1049,44 +1082,46 @@ export const CodeIndexPopover: React.FC = ({ {/* Action Buttons */}
- {(indexingStatus.systemStatus === "Error" || - indexingStatus.systemStatus === "Standby") && ( - vscode.postMessage({ type: "startIndexing" })} - disabled={saveStatus === "saving" || hasUnsavedChanges}> - {t("settings:codeIndex.startIndexingButton")} - - )} - - {(indexingStatus.systemStatus === "Indexed" || - indexingStatus.systemStatus === "Error") && ( - - - - {t("settings:codeIndex.clearIndexDataButton")} - - - - - - {t("settings:codeIndex.clearDataDialog.title")} - - - {t("settings:codeIndex.clearDataDialog.description")} - - - - - {t("settings:codeIndex.clearDataDialog.cancelButton")} - - vscode.postMessage({ type: "clearIndexData" })}> - {t("settings:codeIndex.clearDataDialog.confirmButton")} - - - - - )} + {currentSettings.codebaseIndexEnabled && + (indexingStatus.systemStatus === "Error" || + indexingStatus.systemStatus === "Standby") && ( + vscode.postMessage({ type: "startIndexing" })} + disabled={saveStatus === "saving" || hasUnsavedChanges}> + {t("settings:codeIndex.startIndexingButton")} + + )} + + {currentSettings.codebaseIndexEnabled && + (indexingStatus.systemStatus === "Indexed" || + indexingStatus.systemStatus === "Error") && ( + + + + {t("settings:codeIndex.clearIndexDataButton")} + + + + + + {t("settings:codeIndex.clearDataDialog.title")} + + + {t("settings:codeIndex.clearDataDialog.description")} + + + + + {t("settings:codeIndex.clearDataDialog.cancelButton")} + + vscode.postMessage({ type: "clearIndexData" })}> + {t("settings:codeIndex.clearDataDialog.confirmButton")} + + + + + )}
({ {children} ), + VSCodeCheckbox: ({ checked, onChange, children, ...rest }: any) => ( + + ), })) // Helper function to simulate input on form elements diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 57b5dadaaef..25435b2c9b6 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "Indexació de codi", "enableLabel": "Habilitar indexació de codi", - "enableDescription": "<0>Indexació de codi és una característica experimental que crea un índex de cerca semàntica del vostre projecte utilitzant embeddings d'IA. Això permet a Roo Code entendre millor i navegar per grans bases de codi trobant codi rellevant basat en significat en lloc de només paraules clau.", + "enableDescription": "Habilita la indexació de codi per millorar la cerca i la comprensió del context", "providerLabel": "Proveïdor d'embeddings", "selectProviderPlaceholder": "Seleccionar proveïdor", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 8d57652ba2d..760541a5cd2 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -41,7 +41,7 @@ "description": "Konfiguriere Codebase-Indexierungseinstellungen, um semantische Suche in deinem Projekt zu aktivieren. <0>Mehr erfahren", "statusTitle": "Status", "enableLabel": "Codebase-Indexierung aktivieren", - "enableDescription": "<0>Codebase-Indexierung ist eine experimentelle Funktion, die einen semantischen Suchindex deines Projekts mit KI-Embeddings erstellt. Dies ermöglicht es Roo Code, große Codebasen besser zu verstehen und zu navigieren, indem relevanter Code basierend auf Bedeutung statt nur Schlüsselwörtern gefunden wird.", + "enableDescription": "Aktiviere die Code-Indizierung für eine verbesserte Suche und ein besseres Kontextverständnis", "settingsTitle": "Indexierungseinstellungen", "disabledMessage": "Codebase-Indexierung ist derzeit deaktiviert. Aktiviere sie in den globalen Einstellungen, um Indexierungsoptionen zu konfigurieren.", "providerLabel": "Embeddings-Anbieter", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index da40058b00d..c1b63fbb66f 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -41,7 +41,7 @@ "description": "Configure codebase indexing settings to enable semantic search of your project. <0>Learn more", "statusTitle": "Status", "enableLabel": "Enable Codebase Indexing", - "enableDescription": "<0>Codebase Indexing is an experimental feature that creates a semantic search index of your project using AI embeddings. This enables Roo Code to better understand and navigate large codebases by finding relevant code based on meaning rather than just keywords.", + "enableDescription": "Enable code indexing for improved search and context understanding", "settingsTitle": "Indexing Settings", "disabledMessage": "Codebase indexing is currently disabled. Enable it in the global settings to configure indexing options.", "providerLabel": "Embeddings Provider", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index b91d0e055f5..f7e9e8f1303 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -41,7 +41,7 @@ "description": "Configura los ajustes de indexación de código para habilitar búsqueda semántica en tu proyecto. <0>Más información", "statusTitle": "Estado", "enableLabel": "Habilitar indexación de código", - "enableDescription": "<0>La indexación de código es una función experimental que crea un índice de búsqueda semántica de tu proyecto usando embeddings de IA. Esto permite a Roo Code entender mejor y navegar grandes bases de código encontrando código relevante basado en significado en lugar de solo palabras clave.", + "enableDescription": "Habilita la indexación de código para mejorar la búsqueda y la comprensión del contexto", "settingsTitle": "Configuración de indexación", "disabledMessage": "La indexación de código está actualmente deshabilitada. Habilítala en la configuración global para configurar las opciones de indexación.", "providerLabel": "Proveedor de embeddings", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index d4940567c6e..e1d236bd790 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -41,7 +41,7 @@ "description": "Configurez les paramètres d'indexation de la base de code pour activer la recherche sémantique dans votre projet. <0>En savoir plus", "statusTitle": "Statut", "enableLabel": "Activer l'indexation de la base de code", - "enableDescription": "<0>L'indexation de la base de code est une fonctionnalité expérimentale qui crée un index de recherche sémantique de votre projet en utilisant des embeddings IA. Cela permet à Roo Code de mieux comprendre et naviguer dans de grandes bases de code en trouvant du code pertinent basé sur le sens plutôt que seulement sur des mots-clés.", + "enableDescription": "Activer l'indexation du code pour une recherche et une compréhension du contexte améliorées", "settingsTitle": "Paramètres d'indexation", "disabledMessage": "L'indexation de la base de code est actuellement désactivée. Activez-la dans les paramètres globaux pour configurer les options d'indexation.", "providerLabel": "Fournisseur d'embeddings", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 8665afc9909..c2b8d8593a2 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "कोडबेस इंडेक्सिंग", "enableLabel": "कोडबेस इंडेक्सिंग सक्षम करें", - "enableDescription": "<0>कोडबेस इंडेक्सिंग एक प्रयोगात्मक सुविधा है जो AI एम्बेडिंग का उपयोग करके आपके प्रोजेक्ट का सिमेंटिक सर्च इंडेक्स बनाती है। यह Roo Code को केवल कीवर्ड के बजाय अर्थ के आधार पर संबंधित कोड खोजकर बड़े कोडबेस को बेहतर तरीके से समझने और नेविगेट करने में सक्षम बनाता है।", + "enableDescription": "बेहतर खोज और संदर्भ समझने के लिए कोड इंडेक्सिंग सक्षम करें", "providerLabel": "एम्बेडिंग प्रदाता", "selectProviderPlaceholder": "प्रदाता चुनें", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 199751e384e..24bba83fc71 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "Pengindeksan Codebase", "enableLabel": "Aktifkan Pengindeksan Codebase", - "enableDescription": "<0>Pengindeksan Codebase adalah fitur eksperimental yang membuat indeks pencarian semantik dari proyek kamu menggunakan AI embeddings. Ini memungkinkan Roo Code untuk lebih memahami dan menavigasi codebase besar dengan menemukan kode yang relevan berdasarkan makna daripada hanya kata kunci.", + "enableDescription": "Aktifkan pengindeksan kode untuk pencarian dan pemahaman konteks yang lebih baik", "providerLabel": "Provider Embeddings", "selectProviderPlaceholder": "Pilih provider", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 48a4c8e4db4..7b1e26acc75 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "Indicizzazione del codice", "enableLabel": "Abilita indicizzazione del codice", - "enableDescription": "<0>L'indicizzazione del codice è una funzionalità sperimentale che crea un indice di ricerca semantica del tuo progetto utilizzando embedding AI. Questo permette a Roo Code di comprendere meglio e navigare grandi basi di codice trovando codice rilevante basato sul significato piuttosto che solo su parole chiave.", + "enableDescription": "Abilita l'indicizzazione del codice per una ricerca e una comprensione del contesto migliorate", "providerLabel": "Fornitore di embedding", "selectProviderPlaceholder": "Seleziona fornitore", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 397ce67f62c..5114719bb20 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "コードベースのインデックス作成", "enableLabel": "コードベースのインデックス作成を有効化", - "enableDescription": "<0>コードベースのインデックス作成は、AIエンベディングを使用してプロジェクトのセマンティック検索インデックスを作成する実験的機能です。これにより、Roo Codeは単なるキーワードではなく意味に基づいて関連するコードを見つけることで、大規模なコードベースをより良く理解し、ナビゲートできるようになります。", + "enableDescription": "コードのインデックス作成を有効にして、検索とコンテキストの理解を向上させます", "providerLabel": "埋め込みプロバイダー", "selectProviderPlaceholder": "プロバイダーを選択", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 746cea65ad4..59bedc403f0 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "코드베이스 인덱싱", "enableLabel": "코드베이스 인덱싱 활성화", - "enableDescription": "<0>코드베이스 인덱싱은 AI 임베딩을 사용하여 프로젝트의 의미론적 검색 인덱스를 생성하는 실험적 기능입니다. 이를 통해 Roo Code는 단순한 키워드가 아닌 의미를 기반으로 관련 코드를 찾아 대규모 코드베이스를 더 잘 이해하고 탐색할 수 있습니다.", + "enableDescription": "향상된 검색 및 컨텍스트 이해를 위해 코드 인덱싱 활성화", "providerLabel": "임베딩 제공자", "selectProviderPlaceholder": "제공자 선택", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index c5315205cae..f3d6ed3b962 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "Codebase indexering", "enableLabel": "Codebase indexering inschakelen", - "enableDescription": "<0>Codebase indexering is een experimentele functie die een semantische zoekindex van je project creëert met behulp van AI-embeddings. Dit stelt Roo Code in staat om grote codebases beter te begrijpen en te navigeren door relevante code te vinden op basis van betekenis in plaats van alleen trefwoorden.", + "enableDescription": "Code-indexering inschakelen voor verbeterde zoekresultaten en contextbegrip", "providerLabel": "Embeddings provider", "selectProviderPlaceholder": "Selecteer provider", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 1829c23ba4d..edf9a6bfb31 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "Indeksowanie kodu", "enableLabel": "Włącz indeksowanie kodu", - "enableDescription": "<0>Indeksowanie kodu to eksperymentalna funkcja, która tworzy semantyczny indeks wyszukiwania Twojego projektu przy użyciu osadzeń AI. Umożliwia to Roo Code lepsze zrozumienie i nawigację po dużych bazach kodu poprzez znajdowanie odpowiedniego kodu na podstawie znaczenia, a nie tylko słów kluczowych.", + "enableDescription": "Włącz indeksowanie kodu, aby poprawić wyszukiwanie i zrozumienie kontekstu", "providerLabel": "Dostawca osadzania", "selectProviderPlaceholder": "Wybierz dostawcę", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 6e46cc8c3e9..6b7fd29a142 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "Indexação de Código", "enableLabel": "Ativar Indexação de Código", - "enableDescription": "<0>Indexação de Código é um recurso experimental que cria um índice de busca semântica do seu projeto usando embeddings de IA. Isso permite ao Roo Code entender melhor e navegar grandes bases de código encontrando código relevante baseado em significado ao invés de apenas palavras-chave.", + "enableDescription": "Ative a indexação de código para pesquisa e compreensão de contexto aprimoradas", "providerLabel": "Provedor de Embeddings", "selectProviderPlaceholder": "Selecionar provedor", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index e0a897c2e5f..c3f410a1eaf 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "Индексация кодовой базы", "enableLabel": "Включить индексацию кодовой базы", - "enableDescription": "<0>Индексация кодовой базы — это экспериментальная функция, которая создает семантический поисковый индекс вашего проекта с использованием ИИ-эмбеддингов. Это позволяет Roo Code лучше понимать и навигировать по большим кодовым базам, находя релевантный код на основе смысла, а не только ключевых слов.", + "enableDescription": "Включите индексацию кода для улучшения поиска и понимания контекста", "providerLabel": "Провайдер эмбеддингов", "selectProviderPlaceholder": "Выберите провайдера", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 486991ec0dd..bde23ae5f45 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "Kod Tabanı İndeksleme", "enableLabel": "Kod Tabanı İndekslemeyi Etkinleştir", - "enableDescription": "<0>Kod Tabanı İndeksleme, AI gömme teknolojisini kullanarak projenizin semantik arama indeksini oluşturan deneysel bir özelliktir. Bu, Roo Code'un sadece anahtar kelimeler yerine anlam temelinde ilgili kodu bularak büyük kod tabanlarını daha iyi anlamasını ve gezinmesini sağlar.", + "enableDescription": "Geliştirilmiş arama ve bağlam anlayışı için kod indekslemeyi etkinleştirin", "providerLabel": "Gömme Sağlayıcısı", "selectProviderPlaceholder": "Sağlayıcı seç", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index e31355b4039..bf3948d4387 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "Lập chỉ mục mã nguồn", "enableLabel": "Bật lập chỉ mục mã nguồn", - "enableDescription": "<0>Lập chỉ mục mã nguồn là một tính năng thử nghiệm tạo ra chỉ mục tìm kiếm ngữ nghĩa cho dự án của bạn bằng cách sử dụng AI embeddings. Điều này cho phép Roo Code hiểu rõ hơn và điều hướng các codebase lớn bằng cách tìm mã liên quan dựa trên ý nghĩa thay vì chỉ từ khóa.", + "enableDescription": "Bật lập chỉ mục mã để cải thiện tìm kiếm và sự hiểu biết về ngữ cảnh", "providerLabel": "Nhà cung cấp nhúng", "selectProviderPlaceholder": "Chọn nhà cung cấp", "openaiProvider": "OpenAI", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 4b46b9af0a4..24c8a889542 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -41,7 +41,7 @@ "description": "配置代码库索引设置以启用项目的语义搜索。<0>了解更多", "statusTitle": "状态", "enableLabel": "启用代码库索引", - "enableDescription": "<0>代码库索引是一个实验性功能,使用 AI 嵌入为您的项目创建语义搜索索引。这使 Roo Code 能够通过基于含义而非仅仅关键词来查找相关代码,从而更好地理解和导航大型代码库。", + "enableDescription": "启用代码索引以改进搜索和上下文理解", "settingsTitle": "索引设置", "disabledMessage": "代码库索引当前已禁用。在全局设置中启用它以配置索引选项。", "providerLabel": "嵌入提供商", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 3e35097b1ec..ad36be57a89 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -39,7 +39,7 @@ "codeIndex": { "title": "程式碼庫索引", "enableLabel": "啟用程式碼庫索引", - "enableDescription": "<0>程式碼庫索引是一個實驗性功能,使用 AI 嵌入為您的專案建立語義搜尋索引。這使 Roo Code 能夠透過基於含義而非僅僅關鍵詞來尋找相關程式碼,從而更好地理解和導覽大型程式碼庫。", + "enableDescription": "啟用程式碼索引以改進搜尋和上下文理解", "providerLabel": "嵌入提供者", "selectProviderPlaceholder": "選擇提供者", "openaiProvider": "OpenAI",