mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: implement workspace-specific provider settings
- Add workspace-scoped storage support to ProviderSettingsManager - Add toggle between workspace and global settings in UI - Add visual indicators (Globe/Folder icons) for current scope - Add VSCode configuration option for workspace provider settings - Support migration of settings between global and workspace scopes - Update tests to support new workspace settings functionality Fixes #7865
This commit is contained in:
parent
8fee3127ff
commit
c486de84da
10 changed files with 280 additions and 4 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import * as vscode from "vscode"
|
||||
import { ExtensionContext } from "vscode"
|
||||
import { z, ZodError } from "zod"
|
||||
import deepEqual from "fast-deep-equal"
|
||||
|
|
@ -14,6 +15,7 @@ import {
|
|||
import { TelemetryService } from "@roo-code/telemetry"
|
||||
|
||||
import { Mode, modes } from "../../shared/modes"
|
||||
import { getWorkspacePath } from "../../utils/path"
|
||||
|
||||
export interface SyncCloudProfilesResult {
|
||||
hasChanges: boolean
|
||||
|
|
@ -41,6 +43,7 @@ export type ProviderProfiles = z.infer<typeof providerProfilesSchema>
|
|||
|
||||
export class ProviderSettingsManager {
|
||||
private static readonly SCOPE_PREFIX = "roo_cline_config_"
|
||||
private static readonly WORKSPACE_PREFIX = "workspace_"
|
||||
private readonly defaultConfigId = this.generateId()
|
||||
|
||||
private readonly defaultModeApiConfigs: Record<string, string> = Object.fromEntries(
|
||||
|
|
@ -61,14 +64,105 @@ export class ProviderSettingsManager {
|
|||
}
|
||||
|
||||
private readonly context: ExtensionContext
|
||||
private useWorkspaceSettings: boolean = false
|
||||
|
||||
constructor(context: ExtensionContext) {
|
||||
this.context = context
|
||||
|
||||
// Check if workspace settings should be used
|
||||
this.checkWorkspaceSettingsPreference()
|
||||
|
||||
// TODO: We really shouldn't have async methods in the constructor.
|
||||
this.initialize().catch(console.error)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user prefers workspace-specific settings
|
||||
*/
|
||||
private checkWorkspaceSettingsPreference(): void {
|
||||
const config = vscode.workspace.getConfiguration("roo-cline")
|
||||
this.useWorkspaceSettings = config.get<boolean>("useWorkspaceProviderSettings", false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate storage key based on workspace preference
|
||||
*/
|
||||
private getStorageKey(): string {
|
||||
if (this.useWorkspaceSettings) {
|
||||
const workspacePath = getWorkspacePath()
|
||||
if (workspacePath) {
|
||||
// Create a unique key based on workspace path
|
||||
const workspaceId = Buffer.from(workspacePath)
|
||||
.toString("base64")
|
||||
.replace(/[^a-zA-Z0-9]/g, "")
|
||||
.substring(0, 20)
|
||||
return `${ProviderSettingsManager.SCOPE_PREFIX}${ProviderSettingsManager.WORKSPACE_PREFIX}${workspaceId}`
|
||||
}
|
||||
}
|
||||
// Fall back to global settings
|
||||
return `${ProviderSettingsManager.SCOPE_PREFIX}api_config`
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use workspace-specific settings
|
||||
*/
|
||||
public async setUseWorkspaceSettings(useWorkspace: boolean): Promise<void> {
|
||||
const wasUsingWorkspace = this.useWorkspaceSettings
|
||||
this.useWorkspaceSettings = useWorkspace
|
||||
|
||||
// Update VSCode configuration
|
||||
const config = vscode.workspace.getConfiguration("roo-cline")
|
||||
await config.update("useWorkspaceProviderSettings", useWorkspace, vscode.ConfigurationTarget.Global)
|
||||
|
||||
// If switching from global to workspace or vice versa, optionally migrate settings
|
||||
if (wasUsingWorkspace !== useWorkspace) {
|
||||
await this.migrateSettingsBetweenScopes(wasUsingWorkspace, useWorkspace)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate settings between global and workspace scopes
|
||||
*/
|
||||
private async migrateSettingsBetweenScopes(fromWorkspace: boolean, toWorkspace: boolean): Promise<void> {
|
||||
try {
|
||||
// Get settings from the source scope
|
||||
const sourceKey = fromWorkspace ? this.getStorageKey() : `${ProviderSettingsManager.SCOPE_PREFIX}api_config`
|
||||
const targetKey = toWorkspace ? this.getStorageKey() : `${ProviderSettingsManager.SCOPE_PREFIX}api_config`
|
||||
|
||||
if (sourceKey === targetKey) {
|
||||
return // No migration needed
|
||||
}
|
||||
|
||||
const sourceContent = await this.context.secrets.get(sourceKey)
|
||||
if (!sourceContent) {
|
||||
return // No settings to migrate
|
||||
}
|
||||
|
||||
// Check if target already has settings
|
||||
const targetContent = await this.context.secrets.get(targetKey)
|
||||
if (targetContent) {
|
||||
// Target already has settings, don't overwrite
|
||||
console.log(`Target scope already has settings, skipping migration`)
|
||||
return
|
||||
}
|
||||
|
||||
// Migrate the settings
|
||||
await this.context.secrets.store(targetKey, sourceContent)
|
||||
console.log(
|
||||
`Successfully migrated provider settings from ${fromWorkspace ? "workspace" : "global"} to ${toWorkspace ? "workspace" : "global"} scope`,
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`Failed to migrate settings between scopes: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current scope is workspace-specific
|
||||
*/
|
||||
public isUsingWorkspaceSettings(): boolean {
|
||||
return this.useWorkspaceSettings && !!getWorkspacePath()
|
||||
}
|
||||
|
||||
public generateId() {
|
||||
return Math.random().toString(36).substring(2, 15)
|
||||
}
|
||||
|
|
@ -372,6 +466,40 @@ export class ProviderSettingsManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a configuration from global storage regardless of current workspace setting
|
||||
*/
|
||||
public async getConfigFromGlobal(name: string): Promise<ProviderSettingsWithId | null> {
|
||||
try {
|
||||
return await this.lock(async () => {
|
||||
// Temporarily get the global storage key
|
||||
const globalKey = `${ProviderSettingsManager.SCOPE_PREFIX}api_config`
|
||||
const content = await this.context.secrets.get(globalKey)
|
||||
|
||||
if (!content) {
|
||||
return null
|
||||
}
|
||||
|
||||
const providerProfiles = providerProfilesSchema
|
||||
.extend({
|
||||
apiConfigs: z.record(z.string(), z.any()),
|
||||
})
|
||||
.parse(JSON.parse(content))
|
||||
|
||||
const config = providerProfiles.apiConfigs[name]
|
||||
if (!config) {
|
||||
return null
|
||||
}
|
||||
|
||||
const result = providerSettingsWithIdSchema.safeParse(config)
|
||||
return result.success ? result.data : null
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(`Failed to get global config for ${name}:`, error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a profile by name or ID.
|
||||
*/
|
||||
|
|
@ -493,12 +621,12 @@ export class ProviderSettingsManager {
|
|||
*/
|
||||
public async resetAllConfigs() {
|
||||
return await this.lock(async () => {
|
||||
await this.context.secrets.delete(this.secretsKey)
|
||||
await this.context.secrets.delete(this.getStorageKey())
|
||||
})
|
||||
}
|
||||
|
||||
private get secretsKey() {
|
||||
return `${ProviderSettingsManager.SCOPE_PREFIX}api_config`
|
||||
return this.getStorageKey()
|
||||
}
|
||||
|
||||
private async load(): Promise<ProviderProfiles> {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,23 @@ vi.mock("vscode", () => ({
|
|||
Uri: {
|
||||
file: vi.fn((filePath) => ({ fsPath: filePath })),
|
||||
},
|
||||
workspace: {
|
||||
workspaceFolders: undefined,
|
||||
getConfiguration: vi.fn(() => ({
|
||||
get: vi.fn((key) => {
|
||||
if (key === "useWorkspaceProviderSettings") {
|
||||
return false
|
||||
}
|
||||
return undefined
|
||||
}),
|
||||
update: vi.fn(),
|
||||
})),
|
||||
},
|
||||
ConfigurationTarget: {
|
||||
Global: 1,
|
||||
Workspace: 2,
|
||||
WorkspaceFolder: 3,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("fs/promises", () => ({
|
||||
|
|
|
|||
|
|
@ -1795,6 +1795,9 @@ export class ClineProvider
|
|||
const currentMode = mode ?? defaultModeSlug
|
||||
const hasSystemPromptOverride = await this.hasFileBasedSystemPromptOverride(currentMode)
|
||||
|
||||
// Check if using workspace-specific provider settings
|
||||
const isUsingWorkspaceSettings = this.providerSettingsManager.isUsingWorkspaceSettings()
|
||||
|
||||
return {
|
||||
version: this.context.extension?.packageJSON?.version ?? "",
|
||||
apiConfiguration,
|
||||
|
|
@ -1920,6 +1923,7 @@ export class ClineProvider
|
|||
openRouterImageGenerationSelectedModel,
|
||||
openRouterUseMiddleOutTransform,
|
||||
featureRoomoteControlEnabled,
|
||||
isUsingWorkspaceSettings,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -889,6 +889,7 @@ describe("ClineProvider", () => {
|
|||
listConfig: vi.fn().mockResolvedValue([profile]),
|
||||
activateProfile: vi.fn().mockResolvedValue(profile),
|
||||
setModeConfig: vi.fn(),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
// Switch to architect mode
|
||||
|
|
@ -910,6 +911,7 @@ describe("ClineProvider", () => {
|
|||
.fn()
|
||||
.mockResolvedValue([{ name: "current-config", id: "current-id", apiProvider: "anthropic" }]),
|
||||
setModeConfig: vi.fn(),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
provider.setValue("currentApiConfigName", "current-config")
|
||||
|
|
@ -932,6 +934,7 @@ describe("ClineProvider", () => {
|
|||
listConfig: vi.fn().mockResolvedValue([profile]),
|
||||
setModeConfig: vi.fn(),
|
||||
getModeConfigId: vi.fn().mockResolvedValue(undefined),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
// First set the mode
|
||||
|
|
@ -959,6 +962,7 @@ describe("ClineProvider", () => {
|
|||
listConfig: vi.fn().mockResolvedValue([profile]),
|
||||
setModeConfig: vi.fn(),
|
||||
getModeConfigId: vi.fn().mockResolvedValue(undefined),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
// First set the mode
|
||||
|
|
@ -1159,6 +1163,7 @@ describe("ClineProvider", () => {
|
|||
listConfig: vi.fn().mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]),
|
||||
saveConfig: vi.fn().mockResolvedValue("test-id"),
|
||||
setModeConfig: vi.fn(),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
// Update API configuration
|
||||
|
|
@ -1626,6 +1631,7 @@ describe("ClineProvider", () => {
|
|||
listConfig: vi.fn().mockResolvedValue([profile]),
|
||||
activateProfile: vi.fn().mockResolvedValue(profile),
|
||||
setModeConfig: vi.fn(),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
// Switch to architect mode
|
||||
|
|
@ -1650,6 +1656,7 @@ describe("ClineProvider", () => {
|
|||
.fn()
|
||||
.mockResolvedValue([{ name: "current-config", id: "current-id", apiProvider: "anthropic" }]),
|
||||
setModeConfig: vi.fn(),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
// Mock the ContextProxy's getValue method to return the current config name
|
||||
|
|
@ -1707,6 +1714,7 @@ describe("ClineProvider", () => {
|
|||
;(provider as any).providerSettingsManager = {
|
||||
getModeConfigId: vi.fn().mockResolvedValue(undefined),
|
||||
listConfig: vi.fn().mockResolvedValue([]),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
}
|
||||
|
||||
// Spy on log method to verify warning was logged
|
||||
|
|
@ -1776,6 +1784,7 @@ describe("ClineProvider", () => {
|
|||
activateProfile: vi
|
||||
.fn()
|
||||
.mockResolvedValue({ name: "test-config", id: "config-id", apiProvider: "anthropic" }),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
}
|
||||
|
||||
// Spy on log method to verify no warning was logged
|
||||
|
|
@ -1831,6 +1840,7 @@ describe("ClineProvider", () => {
|
|||
;(provider as any).providerSettingsManager = {
|
||||
getModeConfigId: vi.fn().mockResolvedValue(undefined),
|
||||
listConfig: vi.fn().mockResolvedValue([]),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
}
|
||||
|
||||
// Create history item with built-in mode
|
||||
|
|
@ -1862,6 +1872,7 @@ describe("ClineProvider", () => {
|
|||
;(provider as any).providerSettingsManager = {
|
||||
getModeConfigId: vi.fn().mockResolvedValue(undefined),
|
||||
listConfig: vi.fn().mockResolvedValue([]),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
}
|
||||
|
||||
// Create history item without mode
|
||||
|
|
@ -1909,6 +1920,7 @@ describe("ClineProvider", () => {
|
|||
.fn()
|
||||
.mockResolvedValue([{ name: "test-config", id: "config-id", apiProvider: "anthropic" }]),
|
||||
activateProfile: vi.fn().mockRejectedValue(new Error("Failed to load config")),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
}
|
||||
|
||||
// Spy on log method
|
||||
|
|
@ -2008,6 +2020,7 @@ describe("ClineProvider", () => {
|
|||
listConfig: vi
|
||||
.fn()
|
||||
.mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
// Mock getState to provide necessary data
|
||||
|
|
@ -2040,6 +2053,7 @@ describe("ClineProvider", () => {
|
|||
listConfig: vi
|
||||
.fn()
|
||||
.mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
const testApiConfig = {
|
||||
|
|
@ -2083,6 +2097,7 @@ describe("ClineProvider", () => {
|
|||
listConfig: vi
|
||||
.fn()
|
||||
.mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
// Setup Task instance with auto-mock from the top of the file
|
||||
|
|
@ -2124,6 +2139,7 @@ describe("ClineProvider", () => {
|
|||
listConfig: vi
|
||||
.fn()
|
||||
.mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]),
|
||||
isUsingWorkspaceSettings: vi.fn().mockReturnValue(false),
|
||||
} as any
|
||||
|
||||
const testApiConfig = {
|
||||
|
|
|
|||
|
|
@ -1809,6 +1809,58 @@ export const webviewMessageHandler = async (
|
|||
}
|
||||
}
|
||||
break
|
||||
case "toggleWorkspaceProviderSettings":
|
||||
// Toggle between workspace and global provider settings
|
||||
try {
|
||||
const useWorkspace = message.bool ?? false
|
||||
const migrateSettings = message.values?.migrateSettings ?? false
|
||||
|
||||
// Update the configuration setting
|
||||
await vscode.workspace
|
||||
.getConfiguration(Package.name)
|
||||
.update("useWorkspaceProviderSettings", useWorkspace, vscode.ConfigurationTarget.Global)
|
||||
|
||||
// Update the provider settings manager (pass migrateSettings as second parameter)
|
||||
await provider.providerSettingsManager.setUseWorkspaceSettings(useWorkspace)
|
||||
|
||||
// If migration is requested, handle it separately
|
||||
if (migrateSettings && useWorkspace) {
|
||||
// When switching to workspace, optionally copy global settings
|
||||
const globalConfig = await provider.providerSettingsManager.getConfigFromGlobal(
|
||||
getGlobalState("currentApiConfigName") || "default",
|
||||
)
|
||||
if (globalConfig) {
|
||||
await provider.providerSettingsManager.saveConfig(
|
||||
getGlobalState("currentApiConfigName") || "default",
|
||||
globalConfig,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Reload the current configuration
|
||||
const currentConfigName = getGlobalState("currentApiConfigName")
|
||||
if (currentConfigName) {
|
||||
await provider.activateProviderProfile({ name: currentConfigName })
|
||||
}
|
||||
|
||||
// Post updated state to webview (isUsingWorkspaceSettings is already part of ExtensionState)
|
||||
await provider.postStateToWebview()
|
||||
|
||||
// Show confirmation message
|
||||
const scope = useWorkspace ? "workspace" : "global"
|
||||
vscode.window.showInformationMessage(
|
||||
t(`common:info.provider_settings_switched_to_${scope}`) ||
|
||||
`Provider settings switched to ${scope} scope`,
|
||||
)
|
||||
} catch (error) {
|
||||
provider.log(
|
||||
`Error toggling workspace provider settings: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
|
||||
)
|
||||
vscode.window.showErrorMessage(
|
||||
t("common:errors.toggle_workspace_settings") || "Failed to toggle workspace provider settings",
|
||||
)
|
||||
}
|
||||
break
|
||||
case "upsertApiConfiguration":
|
||||
if (message.text && message.apiConfiguration) {
|
||||
await provider.upsertProviderProfile(message.text, message.apiConfiguration)
|
||||
|
|
|
|||
|
|
@ -407,6 +407,11 @@
|
|||
"minimum": 1,
|
||||
"maximum": 200,
|
||||
"description": "%settings.codeIndex.embeddingBatchSize.description%"
|
||||
},
|
||||
"roo-cline.useWorkspaceProviderSettings": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%settings.useWorkspaceProviderSettings.description%"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,5 +41,6 @@
|
|||
"settings.useAgentRules.description": "Enable loading of AGENTS.md files for agent-specific rules (see https://agent-rules.org/)",
|
||||
"settings.apiRequestTimeout.description": "Maximum time in seconds to wait for API responses (0 = no timeout, 1-3600s, default: 600s). Higher values are recommended for local providers like LM Studio and Ollama that may need more processing time.",
|
||||
"settings.newTaskRequireTodos.description": "Require todos parameter when creating new tasks with the new_task tool",
|
||||
"settings.codeIndex.embeddingBatchSize.description": "The batch size for embedding operations during code indexing. Adjust this based on your API provider's limits. Default is 60."
|
||||
"settings.codeIndex.embeddingBatchSize.description": "The batch size for embedding operations during code indexing. Adjust this based on your API provider's limits. Default is 60.",
|
||||
"settings.useWorkspaceProviderSettings.description": "Save AI provider configurations per workspace instead of globally. When enabled, each workspace will have its own provider settings."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -347,6 +347,7 @@ export type ExtensionState = Pick<
|
|||
remoteControlEnabled: boolean
|
||||
taskSyncEnabled: boolean
|
||||
featureRoomoteControlEnabled: boolean
|
||||
isUsingWorkspaceSettings?: boolean
|
||||
}
|
||||
|
||||
export interface ClineSayTool {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export interface WebviewMessage {
|
|||
| "loadApiConfigurationById"
|
||||
| "renameApiConfiguration"
|
||||
| "getListApiConfiguration"
|
||||
| "toggleWorkspaceProviderSettings"
|
||||
| "customInstructions"
|
||||
| "allowedCommands"
|
||||
| "deniedCommands"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { memo, useEffect, useRef, useState } from "react"
|
||||
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import { AlertTriangle } from "lucide-react"
|
||||
import { AlertTriangle, Globe, FolderOpen } from "lucide-react"
|
||||
|
||||
import type { ProviderSettingsEntry, OrganizationAllowList } from "@roo-code/types"
|
||||
|
||||
import { useAppTranslation } from "@/i18n/TranslationContext"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { vscode } from "@/utils/vscode"
|
||||
import {
|
||||
type SearchableSelectOption,
|
||||
Button,
|
||||
|
|
@ -14,6 +16,7 @@ import {
|
|||
DialogTitle,
|
||||
StandardTooltip,
|
||||
SearchableSelect,
|
||||
ToggleSwitch,
|
||||
} from "@/components/ui"
|
||||
|
||||
interface ApiConfigManagerProps {
|
||||
|
|
@ -36,6 +39,7 @@ const ApiConfigManager = ({
|
|||
onUpsertConfig,
|
||||
}: ApiConfigManagerProps) => {
|
||||
const { t } = useAppTranslation()
|
||||
const { isUsingWorkspaceSettings } = useExtensionState()
|
||||
|
||||
const [isRenaming, setIsRenaming] = useState(false)
|
||||
const [isCreating, setIsCreating] = useState(false)
|
||||
|
|
@ -292,6 +296,53 @@ const ApiConfigManager = ({
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Workspace vs Global Settings Toggle */}
|
||||
<div className="flex items-center justify-between mt-3 p-2 rounded bg-vscode-editor-background border border-vscode-panel-border">
|
||||
<div className="flex items-center gap-2">
|
||||
{isUsingWorkspaceSettings ? (
|
||||
<FolderOpen size={16} className="text-vscode-foreground" />
|
||||
) : (
|
||||
<Globe size={16} className="text-vscode-foreground" />
|
||||
)}
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium">
|
||||
{isUsingWorkspaceSettings
|
||||
? t("settings:providers.workspaceSettings") || "Workspace Settings"
|
||||
: t("settings:providers.globalSettings") || "Global Settings"}
|
||||
</span>
|
||||
<span className="text-xs text-vscode-descriptionForeground">
|
||||
{isUsingWorkspaceSettings
|
||||
? t("settings:providers.workspaceSettingsDesc") ||
|
||||
"Settings apply only to this workspace"
|
||||
: t("settings:providers.globalSettingsDesc") ||
|
||||
"Settings apply to all workspaces"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<StandardTooltip
|
||||
content={
|
||||
isUsingWorkspaceSettings
|
||||
? t("settings:providers.switchToGlobal") || "Switch to global settings"
|
||||
: t("settings:providers.switchToWorkspace") || "Switch to workspace settings"
|
||||
}>
|
||||
<ToggleSwitch
|
||||
checked={isUsingWorkspaceSettings || false}
|
||||
onChange={() => {
|
||||
vscode.postMessage({
|
||||
type: "toggleWorkspaceProviderSettings",
|
||||
bool: !isUsingWorkspaceSettings,
|
||||
values: { migrateSettings: false },
|
||||
})
|
||||
}}
|
||||
aria-label={
|
||||
isUsingWorkspaceSettings ? "Using workspace settings" : "Using global settings"
|
||||
}
|
||||
disabled={false}
|
||||
/>
|
||||
</StandardTooltip>
|
||||
</div>
|
||||
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-1">
|
||||
{t("settings:providers.description")}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue