This commit is contained in:
roomote-v0[bot] 2026-04-09 03:19:36 +00:00 committed by GitHub
commit 7a3be88323
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 297 additions and 80 deletions

View file

@ -536,6 +536,7 @@ export interface WebviewMessage {
| "checkRulesDirectory"
| "checkRulesDirectoryResult"
| "saveCodeIndexSettingsAtomic"
| "setUseWorkspaceConfig"
| "requestCodeIndexSecretStatus"
| "requestCommands"
| "openCommandFile"
@ -648,6 +649,8 @@ export interface WebviewMessage {
organizationId?: string | null // For organization switching
useProviderSignup?: boolean // For rooCloudSignIn to use provider signup flow
codeIndexSettings?: {
// Whether to save as workspace-specific config
useWorkspaceConfig?: boolean
// Global state settings
codebaseIndexEnabled: boolean
codebaseIndexQdrantUrl: string
@ -750,6 +753,7 @@ export interface IndexingStatus {
workspacePath?: string
workspaceEnabled?: boolean
autoEnableDefault?: boolean
useWorkspaceConfig?: boolean
}
export interface IndexingStatusUpdateMessage {

View file

@ -2321,20 +2321,25 @@ export class ClineProvider
organizationSettingsVersion,
customCondensingPrompt,
codebaseIndexModels: codebaseIndexModels ?? EMBEDDING_MODEL_PROFILES,
codebaseIndexConfig: {
codebaseIndexEnabled: codebaseIndexConfig?.codebaseIndexEnabled ?? false,
codebaseIndexQdrantUrl: codebaseIndexConfig?.codebaseIndexQdrantUrl ?? "http://localhost:6333",
codebaseIndexEmbedderProvider: codebaseIndexConfig?.codebaseIndexEmbedderProvider ?? "openai",
codebaseIndexEmbedderBaseUrl: codebaseIndexConfig?.codebaseIndexEmbedderBaseUrl ?? "",
codebaseIndexEmbedderModelId: codebaseIndexConfig?.codebaseIndexEmbedderModelId ?? "",
codebaseIndexEmbedderModelDimension: codebaseIndexConfig?.codebaseIndexEmbedderModelDimension ?? 1536,
codebaseIndexOpenAiCompatibleBaseUrl: codebaseIndexConfig?.codebaseIndexOpenAiCompatibleBaseUrl,
codebaseIndexSearchMaxResults: codebaseIndexConfig?.codebaseIndexSearchMaxResults,
codebaseIndexSearchMinScore: codebaseIndexConfig?.codebaseIndexSearchMinScore,
codebaseIndexBedrockRegion: codebaseIndexConfig?.codebaseIndexBedrockRegion,
codebaseIndexBedrockProfile: codebaseIndexConfig?.codebaseIndexBedrockProfile,
codebaseIndexOpenRouterSpecificProvider: codebaseIndexConfig?.codebaseIndexOpenRouterSpecificProvider,
},
codebaseIndexConfig: (() => {
// Use workspace-specific config if the current workspace has it enabled
const currentManager = this.getCurrentWorkspaceCodeIndexManager()
const effectiveConfig = currentManager?.getEffectiveConfig() ?? codebaseIndexConfig
return {
codebaseIndexEnabled: effectiveConfig?.codebaseIndexEnabled ?? false,
codebaseIndexQdrantUrl: effectiveConfig?.codebaseIndexQdrantUrl ?? "http://localhost:6333",
codebaseIndexEmbedderProvider: effectiveConfig?.codebaseIndexEmbedderProvider ?? "openai",
codebaseIndexEmbedderBaseUrl: effectiveConfig?.codebaseIndexEmbedderBaseUrl ?? "",
codebaseIndexEmbedderModelId: effectiveConfig?.codebaseIndexEmbedderModelId ?? "",
codebaseIndexEmbedderModelDimension: effectiveConfig?.codebaseIndexEmbedderModelDimension ?? 1536,
codebaseIndexOpenAiCompatibleBaseUrl: effectiveConfig?.codebaseIndexOpenAiCompatibleBaseUrl,
codebaseIndexSearchMaxResults: effectiveConfig?.codebaseIndexSearchMaxResults,
codebaseIndexSearchMinScore: effectiveConfig?.codebaseIndexSearchMinScore,
codebaseIndexBedrockRegion: effectiveConfig?.codebaseIndexBedrockRegion,
codebaseIndexBedrockProfile: effectiveConfig?.codebaseIndexBedrockProfile,
codebaseIndexOpenRouterSpecificProvider: effectiveConfig?.codebaseIndexOpenRouterSpecificProvider,
}
})(),
// Only set mdmCompliant if there's an actual MDM policy
// undefined means no MDM policy, true means compliant, false means non-compliant
mdmCompliant: this.mdmService?.requiresCloudAuth() ? this.checkMdmCompliance() : undefined,
@ -2542,25 +2547,25 @@ export class ClineProvider
organizationSettingsVersion,
customCondensingPrompt: stateValues.customCondensingPrompt,
codebaseIndexModels: stateValues.codebaseIndexModels ?? EMBEDDING_MODEL_PROFILES,
codebaseIndexConfig: {
codebaseIndexEnabled: stateValues.codebaseIndexConfig?.codebaseIndexEnabled ?? false,
codebaseIndexQdrantUrl:
stateValues.codebaseIndexConfig?.codebaseIndexQdrantUrl ?? "http://localhost:6333",
codebaseIndexEmbedderProvider:
stateValues.codebaseIndexConfig?.codebaseIndexEmbedderProvider ?? "openai",
codebaseIndexEmbedderBaseUrl: stateValues.codebaseIndexConfig?.codebaseIndexEmbedderBaseUrl ?? "",
codebaseIndexEmbedderModelId: stateValues.codebaseIndexConfig?.codebaseIndexEmbedderModelId ?? "",
codebaseIndexEmbedderModelDimension:
stateValues.codebaseIndexConfig?.codebaseIndexEmbedderModelDimension,
codebaseIndexOpenAiCompatibleBaseUrl:
stateValues.codebaseIndexConfig?.codebaseIndexOpenAiCompatibleBaseUrl,
codebaseIndexSearchMaxResults: stateValues.codebaseIndexConfig?.codebaseIndexSearchMaxResults,
codebaseIndexSearchMinScore: stateValues.codebaseIndexConfig?.codebaseIndexSearchMinScore,
codebaseIndexBedrockRegion: stateValues.codebaseIndexConfig?.codebaseIndexBedrockRegion,
codebaseIndexBedrockProfile: stateValues.codebaseIndexConfig?.codebaseIndexBedrockProfile,
codebaseIndexOpenRouterSpecificProvider:
stateValues.codebaseIndexConfig?.codebaseIndexOpenRouterSpecificProvider,
},
codebaseIndexConfig: (() => {
// Use workspace-specific config if the current workspace has it enabled
const wsManager = this.getCurrentWorkspaceCodeIndexManager()
const effectiveCfg = wsManager?.getEffectiveConfig() ?? stateValues.codebaseIndexConfig
return {
codebaseIndexEnabled: effectiveCfg?.codebaseIndexEnabled ?? false,
codebaseIndexQdrantUrl: effectiveCfg?.codebaseIndexQdrantUrl ?? "http://localhost:6333",
codebaseIndexEmbedderProvider: effectiveCfg?.codebaseIndexEmbedderProvider ?? "openai",
codebaseIndexEmbedderBaseUrl: effectiveCfg?.codebaseIndexEmbedderBaseUrl ?? "",
codebaseIndexEmbedderModelId: effectiveCfg?.codebaseIndexEmbedderModelId ?? "",
codebaseIndexEmbedderModelDimension: effectiveCfg?.codebaseIndexEmbedderModelDimension,
codebaseIndexOpenAiCompatibleBaseUrl: effectiveCfg?.codebaseIndexOpenAiCompatibleBaseUrl,
codebaseIndexSearchMaxResults: effectiveCfg?.codebaseIndexSearchMaxResults,
codebaseIndexSearchMinScore: effectiveCfg?.codebaseIndexSearchMinScore,
codebaseIndexBedrockRegion: effectiveCfg?.codebaseIndexBedrockRegion,
codebaseIndexBedrockProfile: effectiveCfg?.codebaseIndexBedrockProfile,
codebaseIndexOpenRouterSpecificProvider: effectiveCfg?.codebaseIndexOpenRouterSpecificProvider,
}
})(),
profileThresholds: stateValues.profileThresholds ?? {},
lockApiConfigAcrossModes: this.context.workspaceState.get("lockApiConfigAcrossModes", false),
includeDiagnosticMessages: stateValues.includeDiagnosticMessages ?? true,

View file

@ -2511,16 +2511,15 @@ export const webviewMessageHandler = async (
}
const settings = message.codeIndexSettings
const saveToWorkspace = settings.useWorkspaceConfig === true
try {
// Check if embedder provider has changed
const currentConfig = getGlobalState("codebaseIndexConfig") || {}
const embedderProviderChanged =
currentConfig.codebaseIndexEmbedderProvider !== settings.codebaseIndexEmbedderProvider
const currentCodeIndexManager = provider.getCurrentWorkspaceCodeIndexManager()
// Save global state settings atomically
const globalStateConfig = {
...currentConfig,
// Build the config object (used for both global and workspace saves)
const configObject = {
codebaseIndexEnabled: settings.codebaseIndexEnabled,
codebaseIndexQdrantUrl: settings.codebaseIndexQdrantUrl,
codebaseIndexEmbedderProvider: settings.codebaseIndexEmbedderProvider,
@ -2535,59 +2534,71 @@ export const webviewMessageHandler = async (
codebaseIndexOpenRouterSpecificProvider: settings.codebaseIndexOpenRouterSpecificProvider,
}
// Save global state first
await updateGlobalState("codebaseIndexConfig", globalStateConfig)
// Save secrets directly using context proxy
// Build secrets map
const secretEntries: Record<string, string> = {}
if (settings.codeIndexOpenAiKey !== undefined) {
await provider.contextProxy.storeSecret("codeIndexOpenAiKey", settings.codeIndexOpenAiKey)
secretEntries.codeIndexOpenAiKey = settings.codeIndexOpenAiKey
}
if (settings.codeIndexQdrantApiKey !== undefined) {
await provider.contextProxy.storeSecret("codeIndexQdrantApiKey", settings.codeIndexQdrantApiKey)
secretEntries.codeIndexQdrantApiKey = settings.codeIndexQdrantApiKey
}
if (settings.codebaseIndexOpenAiCompatibleApiKey !== undefined) {
await provider.contextProxy.storeSecret(
"codebaseIndexOpenAiCompatibleApiKey",
settings.codebaseIndexOpenAiCompatibleApiKey,
)
secretEntries.codebaseIndexOpenAiCompatibleApiKey = settings.codebaseIndexOpenAiCompatibleApiKey
}
if (settings.codebaseIndexGeminiApiKey !== undefined) {
await provider.contextProxy.storeSecret(
"codebaseIndexGeminiApiKey",
settings.codebaseIndexGeminiApiKey,
)
secretEntries.codebaseIndexGeminiApiKey = settings.codebaseIndexGeminiApiKey
}
if (settings.codebaseIndexMistralApiKey !== undefined) {
await provider.contextProxy.storeSecret(
"codebaseIndexMistralApiKey",
settings.codebaseIndexMistralApiKey,
)
secretEntries.codebaseIndexMistralApiKey = settings.codebaseIndexMistralApiKey
}
if (settings.codebaseIndexVercelAiGatewayApiKey !== undefined) {
await provider.contextProxy.storeSecret(
"codebaseIndexVercelAiGatewayApiKey",
settings.codebaseIndexVercelAiGatewayApiKey,
)
secretEntries.codebaseIndexVercelAiGatewayApiKey = settings.codebaseIndexVercelAiGatewayApiKey
}
if (settings.codebaseIndexOpenRouterApiKey !== undefined) {
await provider.contextProxy.storeSecret(
"codebaseIndexOpenRouterApiKey",
settings.codebaseIndexOpenRouterApiKey,
)
secretEntries.codebaseIndexOpenRouterApiKey = settings.codebaseIndexOpenRouterApiKey
}
// Determine effective previous config for change detection
const effectivePreviousProvider = saveToWorkspace
? currentCodeIndexManager?.getWorkspaceConfig()?.codebaseIndexEmbedderProvider
: currentConfig.codebaseIndexEmbedderProvider
const embedderProviderChanged = effectivePreviousProvider !== settings.codebaseIndexEmbedderProvider
if (saveToWorkspace && currentCodeIndexManager) {
// Save to workspace-specific storage
await currentCodeIndexManager.saveWorkspaceConfig(configObject)
// Merge new secrets with existing workspace secrets
const existingSecrets = currentCodeIndexManager.getWorkspaceSecrets()
await currentCodeIndexManager.saveWorkspaceSecrets({
...existingSecrets,
...secretEntries,
})
} else {
// Save to global state (existing behavior)
const globalStateConfig = {
...currentConfig,
...configObject,
}
await updateGlobalState("codebaseIndexConfig", globalStateConfig)
// Save secrets to global secret store
for (const [key, value] of Object.entries(secretEntries)) {
await provider.contextProxy.storeSecret(key as any, value)
}
}
// Send success response first - settings are saved regardless of validation
await provider.postMessageToWebview({
type: "codeIndexSettingsSaved",
success: true,
settings: globalStateConfig,
settings: configObject,
})
// Update webview state
await provider.postStateToWebview()
// Then handle validation and initialization for the current workspace
const currentCodeIndexManager = provider.getCurrentWorkspaceCodeIndexManager()
if (currentCodeIndexManager) {
// If embedder provider changed, perform proactive validation
if (embedderProviderChanged) {
@ -2812,6 +2823,42 @@ export const webviewMessageHandler = async (
}
break
}
case "setUseWorkspaceConfig": {
try {
const manager = provider.getCurrentWorkspaceCodeIndexManager()
if (!manager) {
provider.log("Cannot toggle workspace config: No workspace folder open")
return
}
const enabled = message.bool ?? false
await manager.setUseWorkspaceConfig(enabled)
if (enabled && !manager.getWorkspaceConfig()) {
// When enabling workspace config for the first time, copy global config as starting point
const globalConfig = getGlobalState("codebaseIndexConfig") || {}
await manager.saveWorkspaceConfig(globalConfig)
}
// Force config manager re-creation to pick up the new config source
await manager.recoverFromError()
if (manager.isFeatureEnabled && manager.isFeatureConfigured && manager.isWorkspaceEnabled) {
await manager.initialize(provider.contextProxy)
}
provider.postMessageToWebview({
type: "indexingStatusUpdate",
values: manager.getCurrentStatus(),
})
// Refresh webview state so it picks up the effective config
await provider.postStateToWebview()
} catch (error) {
provider.log(
`Error toggling workspace config: ${error instanceof Error ? error.message : String(error)}`,
)
}
break
}
case "setAutoEnableDefault": {
try {
const manager = provider.getCurrentWorkspaceCodeIndexManager()

View file

@ -5,9 +5,20 @@ import { CodeIndexConfig, PreviousConfigSnapshot } from "./interfaces/config"
import { DEFAULT_SEARCH_MIN_SCORE, DEFAULT_MAX_SEARCH_RESULTS } from "./constants"
import { getDefaultModelId, getModelDimension, getModelScoreThreshold } from "../../shared/embeddingModels"
/**
* A function that resolves workspace-specific config and secrets.
* When it returns undefined, the global config is used instead.
*/
export type WorkspaceConfigResolver = () => { config: Record<string, any>; secrets: Record<string, string> } | undefined
/**
* Manages configuration state and validation for the code indexing feature.
* Handles loading, validating, and providing access to configuration values.
*
* Supports both global configuration (via ContextProxy) and per-workspace
* configuration overrides (via WorkspaceConfigResolver). When a workspace
* config resolver is provided and returns a value, it takes priority over
* the global configuration.
*/
export class CodeIndexConfigManager {
private codebaseIndexEnabled: boolean = false
@ -27,7 +38,10 @@ export class CodeIndexConfigManager {
private searchMinScore?: number
private searchMaxResults?: number
constructor(private readonly contextProxy: ContextProxy) {
constructor(
private readonly contextProxy: ContextProxy,
private readonly workspaceConfigResolver?: WorkspaceConfigResolver,
) {
// Initialize with current configuration to avoid false restart triggers
this._loadAndSetConfiguration()
}
@ -44,8 +58,10 @@ export class CodeIndexConfigManager {
* This eliminates code duplication between initializeWithCurrentConfig() and loadConfiguration().
*/
private _loadAndSetConfiguration(): void {
// Load configuration from storage
const codebaseIndexConfig = this.contextProxy?.getGlobalState("codebaseIndexConfig") ?? {
// Check for workspace-specific config first, fall back to global config
const workspaceSource = this.workspaceConfigResolver?.()
const defaultConfig: Record<string, any> = {
codebaseIndexEnabled: false,
codebaseIndexQdrantUrl: "http://localhost:6333",
codebaseIndexEmbedderProvider: "openai",
@ -57,6 +73,12 @@ export class CodeIndexConfigManager {
codebaseIndexBedrockProfile: "",
}
// Load configuration from workspace source or global state.
// Use Record<string, any> to allow flexible property access for both
// workspace config (which is a plain object) and global config.
const codebaseIndexConfig: Record<string, any> =
workspaceSource?.config ?? this.contextProxy?.getGlobalState("codebaseIndexConfig") ?? defaultConfig
const {
codebaseIndexEnabled,
codebaseIndexQdrantUrl,
@ -67,17 +89,25 @@ export class CodeIndexConfigManager {
codebaseIndexSearchMaxResults,
} = codebaseIndexConfig
const openAiKey = this.contextProxy?.getSecret("codeIndexOpenAiKey") ?? ""
const qdrantApiKey = this.contextProxy?.getSecret("codeIndexQdrantApiKey") ?? ""
// Helper to resolve secrets: workspace source takes priority over global secrets
const getSecret = (key: string): string => {
if (workspaceSource?.secrets[key] !== undefined) {
return workspaceSource.secrets[key]
}
return this.contextProxy?.getSecret(key as any) ?? ""
}
const openAiKey = getSecret("codeIndexOpenAiKey")
const qdrantApiKey = getSecret("codeIndexQdrantApiKey")
// Fix: Read OpenAI Compatible settings from the correct location within codebaseIndexConfig
const openAiCompatibleBaseUrl = codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl ?? ""
const openAiCompatibleApiKey = this.contextProxy?.getSecret("codebaseIndexOpenAiCompatibleApiKey") ?? ""
const geminiApiKey = this.contextProxy?.getSecret("codebaseIndexGeminiApiKey") ?? ""
const mistralApiKey = this.contextProxy?.getSecret("codebaseIndexMistralApiKey") ?? ""
const vercelAiGatewayApiKey = this.contextProxy?.getSecret("codebaseIndexVercelAiGatewayApiKey") ?? ""
const openAiCompatibleApiKey = getSecret("codebaseIndexOpenAiCompatibleApiKey")
const geminiApiKey = getSecret("codebaseIndexGeminiApiKey")
const mistralApiKey = getSecret("codebaseIndexMistralApiKey")
const vercelAiGatewayApiKey = getSecret("codebaseIndexVercelAiGatewayApiKey")
const bedrockRegion = codebaseIndexConfig.codebaseIndexBedrockRegion ?? "us-east-1"
const bedrockProfile = codebaseIndexConfig.codebaseIndexBedrockProfile ?? ""
const openRouterApiKey = this.contextProxy?.getSecret("codebaseIndexOpenRouterApiKey") ?? ""
const openRouterApiKey = getSecret("codebaseIndexOpenRouterApiKey")
const openRouterSpecificProvider = codebaseIndexConfig.codebaseIndexOpenRouterSpecificProvider ?? ""
// Update instance variables with configuration

View file

@ -2,7 +2,7 @@ import * as vscode from "vscode"
import { ContextProxy } from "../../core/config/ContextProxy"
import { VectorStoreSearchResult } from "./interfaces"
import { IndexingState } from "./interfaces/manager"
import { CodeIndexConfigManager } from "./config-manager"
import { CodeIndexConfigManager, WorkspaceConfigResolver } from "./config-manager"
import { CodeIndexStateManager } from "./state-manager"
import { CodeIndexServiceFactory } from "./service-factory"
import { CodeIndexSearchService } from "./search-service"
@ -120,6 +120,103 @@ export class CodeIndexManager {
await this.context.globalState.update("codeIndexAutoEnableDefault", enabled)
}
// --- Per-Workspace Config ---
/**
* Returns the workspaceState key for the per-workspace config toggle.
*/
private _useWorkspaceConfigKey(): string {
return "codeIndexUseWorkspaceConfig:" + this._folderUri.toString(true)
}
/**
* Returns the workspaceState key for the per-workspace config object.
*/
private _workspaceConfigKey(): string {
return "codeIndexWorkspaceConfig:" + this._folderUri.toString(true)
}
/**
* Returns the workspaceState key for the per-workspace secrets.
*/
private _workspaceSecretsKey(): string {
return "codeIndexWorkspaceSecrets:" + this._folderUri.toString(true)
}
/**
* Whether this workspace uses workspace-specific config instead of global.
*/
public get useWorkspaceConfig(): boolean {
return this.context.workspaceState.get<boolean>(this._useWorkspaceConfigKey(), false)
}
/**
* Toggle workspace-specific config for this workspace folder.
*/
public async setUseWorkspaceConfig(enabled: boolean): Promise<void> {
await this.context.workspaceState.update(this._useWorkspaceConfigKey(), enabled)
}
/**
* Gets the workspace-specific config object, if any.
*/
public getWorkspaceConfig(): Record<string, any> | undefined {
return this.context.workspaceState.get<Record<string, any>>(this._workspaceConfigKey())
}
/**
* Saves workspace-specific config.
*/
public async saveWorkspaceConfig(config: Record<string, any>): Promise<void> {
await this.context.workspaceState.update(this._workspaceConfigKey(), config)
}
/**
* Gets workspace-specific secrets.
*/
public getWorkspaceSecrets(): Record<string, string> {
return this.context.workspaceState.get<Record<string, string>>(this._workspaceSecretsKey(), {})
}
/**
* Saves workspace-specific secrets.
*/
public async saveWorkspaceSecrets(secrets: Record<string, string>): Promise<void> {
await this.context.workspaceState.update(this._workspaceSecretsKey(), secrets)
}
/**
* Returns the effective config for this workspace (workspace-specific or global).
* Used to populate the webview state with the correct config values.
*/
public getEffectiveConfig(): Record<string, any> | undefined {
if (this.useWorkspaceConfig) {
return this.getWorkspaceConfig()
}
return undefined // caller should fall back to global config
}
/**
* Creates a WorkspaceConfigResolver for this manager instance.
* The resolver lazily loads workspace config when called.
*/
private _createWorkspaceConfigResolver(): WorkspaceConfigResolver | undefined {
// Only create a resolver if workspace config is enabled
if (!this.useWorkspaceConfig) {
return undefined
}
return () => {
const config = this.getWorkspaceConfig()
if (!config) {
return undefined
}
return {
config,
secrets: this.getWorkspaceSecrets(),
}
}
}
public get onProgressUpdate() {
return this._stateManager.onProgressUpdate
}
@ -163,7 +260,7 @@ export class CodeIndexManager {
public async initialize(contextProxy: ContextProxy): Promise<{ requiresRestart: boolean }> {
// 1. ConfigManager Initialization and Configuration Loading
if (!this._configManager) {
this._configManager = new CodeIndexConfigManager(contextProxy)
this._configManager = new CodeIndexConfigManager(contextProxy, this._createWorkspaceConfigResolver())
}
// Load configuration once to get current state and restart requirements
const { requiresRestart } = await this._configManager.loadConfiguration()
@ -331,6 +428,7 @@ export class CodeIndexManager {
workspacePath: this.workspacePath,
workspaceEnabled: this.isWorkspaceEnabled,
autoEnableDefault: this.autoEnableDefault,
useWorkspaceConfig: this.useWorkspaceConfig,
}
}

View file

@ -556,6 +556,9 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
// Always include codebaseIndexEnabled to ensure it's persisted
settingsToSave.codebaseIndexEnabled = currentSettings.codebaseIndexEnabled
// Include workspace config flag so the backend knows where to save
settingsToSave.useWorkspaceConfig = indexingStatus.useWorkspaceConfig ?? false
// Save settings to backend
vscode.postMessage({
type: "saveCodeIndexSettingsAtomic",
@ -1615,7 +1618,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
{/* Workspace Toggle */}
{currentSettings.codebaseIndexEnabled && (
<div className="flex items-center gap-2 pt-1 pb-2">
<div className="flex items-center gap-2 pt-1 pb-1">
<input
type="checkbox"
id="workspace-indexing-toggle"
@ -1636,6 +1639,34 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
</div>
)}
{/* Use Workspace-Specific Settings Toggle */}
{currentSettings.codebaseIndexEnabled && (
<div className="flex items-center gap-2 pt-1 pb-2">
<input
type="checkbox"
id="use-workspace-config-toggle"
checked={indexingStatus.useWorkspaceConfig ?? false}
onChange={(e) =>
vscode.postMessage({
type: "setUseWorkspaceConfig",
bool: e.target.checked,
})
}
className="accent-vscode-focusBorder"
/>
<label
htmlFor="use-workspace-config-toggle"
className="text-xs text-vscode-foreground cursor-pointer">
{t("settings:codeIndex.useWorkspaceConfigLabel")}
</label>
{indexingStatus.useWorkspaceConfig && (
<span className="text-xs text-vscode-descriptionForeground italic">
{t("settings:codeIndex.workspaceConfigActive")}
</span>
)}
</div>
)}
{currentSettings.codebaseIndexEnabled && !indexingStatus.workspaceEnabled && (
<p className="text-xs text-vscode-descriptionForeground pb-2">
{t("settings:codeIndex.workspaceDisabledMessage")}

View file

@ -279,7 +279,9 @@
"stoppingButton": "Stopping...",
"workspaceToggleLabel": "Enable indexing for this workspace",
"workspaceDisabledMessage": "Indexing is configured but not enabled for this workspace.",
"autoEnableDefaultLabel": "Auto-enable indexing for new workspaces"
"autoEnableDefaultLabel": "Auto-enable indexing for new workspaces",
"useWorkspaceConfigLabel": "Use workspace-specific settings",
"workspaceConfigActive": "(settings scoped to this workspace)"
},
"autoApprove": {
"description": "Run these actions without asking for permission. Only enable for actions you fully trust and if you understand the security risks.",