webview-ui/src/components/settings/README.md
This directory contains the refactored API Options components for the Cline extension. The refactoring aims to improve maintainability, code organization, and reduce complexity by separating provider-specific code into modular components.
settings/
├── ApiOptions.tsx # Main component that renders provider-specific components
├── common/ # Reusable UI components
│ ├── ApiKeyField.tsx # API key input with standard styling
│ ├── BaseUrlField.tsx # Base URL input with standard styling
│ ├── ErrorMessage.tsx # Standard error message display
│ ├── ModelInfoView.tsx # Model information display
│ └── ModelSelector.tsx # Model selection dropdown
├── providers/ # Provider-specific components
│ ├── ClineProvider.tsx # Cline configuration
│ ├── AnthropicProvider.tsx # Anthropic-specific configuration
│ ├── BedrockProvider.tsx # AWS Bedrock configuration
│ ├── GeminiProvider.tsx # Google Gemini configuration
│ ├── MistralProvider.tsx # Mistral configuration
│ ├── OllamaProvider.tsx # Ollama configuration
│ ├── OpenAICompatibleProvider.tsx # OpenAI compatible API configuration
│ ├── OpenRouterProvider.tsx # OpenRouter configuration
│ └── ...
└── utils/ # Utility functions
├── pricingUtils.ts # Pricing formatting utilities
└── providerUtils.ts # API configuration normalization
ApiOptions
└── [ProviderComponent] (based on selected provider)
├── ApiKeyField (if needed)
├── BaseUrlField (if needed)
├── ModelSelector (if showing model options)
└── ModelInfoView (if showing model options)
ApiOptions receives the current API configuration from the extension stateapiConfiguration and handleInputChange to manage their statehandleInputChange callbackTo add a new provider:
providers directory, e.g. MyNewProvider.tsximport { ApiConfiguration, myNewProviderModels } from "@shared/api"
import { ApiKeyField } from "../common/ApiKeyField"
import { BaseUrlField } from "../common/BaseUrlField"
import { ModelSelector } from "../common/ModelSelector"
import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
/**
* Props for the MyNewProvider component
*/
interface MyNewProviderProps {
apiConfiguration: ApiConfiguration
handleInputChange: (field: keyof ApiConfiguration) => (event: any) => void
showModelOptions: boolean
isPopup?: boolean
}
/**
* The MyNewProvider configuration component
*/
export const MyNewProvider = ({
apiConfiguration,
handleInputChange,
showModelOptions,
isPopup,
}: MyNewProviderProps) => {
// Get the normalized configuration
const { selectedModelId, selectedModelInfo } = normalizeApiConfiguration(apiConfiguration)
return (
<div>
<ApiKeyField
value={apiConfiguration?.myNewProviderApiKey || ""}
onChange={handleInputChange("myNewProviderApiKey")}
providerName="My New Provider"
signupUrl="https://mynewprovider.com/signup"
/>
<BaseUrlField
value={apiConfiguration?.myNewProviderBaseUrl}
onChange={handleInputChange("myNewProviderBaseUrl")}
defaultPlaceholder="https://api.mynewprovider.com"
/>
{showModelOptions && (
<>
<ModelSelector
models={myNewProviderModels}
selectedModelId={selectedModelId}
onChange={handleInputChange("apiModelId")}
label="Model"
/>
<ModelInfoView
selectedModelId={selectedModelId}
modelInfo={selectedModelInfo}
isPopup={isPopup}
/>
</>
)}
</div>
)
}
ApiOptions.tsx:import { MyNewProvider } from "./providers/MyNewProvider"
// ...
{apiConfiguration && selectedProvider === "mynewprovider" && (
<MyNewProvider
apiConfiguration={apiConfiguration}
handleInputChange={handleInputChange}
showModelOptions={showModelOptions}
isPopup={isPopup}
/>
)}
<VSCodeOption value="mynewprovider">My New Provider</VSCodeOption>
Each provider component should be tested in isolation to ensure it renders correctly and handles user input properly.