import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { memo, useEffect, useRef, useState } from "react" import { ApiConfigMeta } from "../../../../src/shared/ExtensionMessage" import { Dropdown } from "vscrui" import type { DropdownOption } from "vscrui" import { Dialog, DialogContent, DialogTitle } from "../ui/dialog" import { Button, Input } from "../ui" interface ApiConfigManagerProps { currentApiConfigName?: string listApiConfigMeta?: ApiConfigMeta[] onSelectConfig: (configName: string) => void onDeleteConfig: (configName: string) => void onRenameConfig: (oldName: string, newName: string) => void onUpsertConfig: (configName: string) => void } const ApiConfigManager = ({ currentApiConfigName = "", listApiConfigMeta = [], onSelectConfig, onDeleteConfig, onRenameConfig, onUpsertConfig, }: ApiConfigManagerProps) => { const [isRenaming, setIsRenaming] = useState(false) const [isCreating, setIsCreating] = useState(false) const [inputValue, setInputValue] = useState("") const [newProfileName, setNewProfileName] = useState("") const [error, setError] = useState(null) const inputRef = useRef(null) const newProfileInputRef = useRef(null) const validateName = (name: string, isNewProfile: boolean): string | null => { const trimmed = name.trim() if (!trimmed) return "Name cannot be empty" const nameExists = listApiConfigMeta?.some((config) => config.name.toLowerCase() === trimmed.toLowerCase()) // For new profiles, any existing name is invalid if (isNewProfile && nameExists) { return "A profile with this name already exists" } // For rename, only block if trying to rename to a different existing profile if (!isNewProfile && nameExists && trimmed.toLowerCase() !== currentApiConfigName?.toLowerCase()) { return "A profile with this name already exists" } return null } const resetCreateState = () => { setIsCreating(false) setNewProfileName("") setError(null) } const resetRenameState = () => { setIsRenaming(false) setInputValue("") setError(null) } // Focus input when entering rename mode useEffect(() => { if (isRenaming) { const timeoutId = setTimeout(() => inputRef.current?.focus(), 0) return () => clearTimeout(timeoutId) } }, [isRenaming]) // Focus input when opening new dialog useEffect(() => { if (isCreating) { const timeoutId = setTimeout(() => newProfileInputRef.current?.focus(), 0) return () => clearTimeout(timeoutId) } }, [isCreating]) // Reset state when current profile changes useEffect(() => { resetCreateState() resetRenameState() }, [currentApiConfigName]) const handleAdd = () => { resetCreateState() setIsCreating(true) } const handleStartRename = () => { setIsRenaming(true) setInputValue(currentApiConfigName || "") setError(null) } const handleCancel = () => { resetRenameState() } const handleSave = () => { const trimmedValue = inputValue.trim() const error = validateName(trimmedValue, false) if (error) { setError(error) return } if (isRenaming && currentApiConfigName) { if (currentApiConfigName === trimmedValue) { resetRenameState() return } onRenameConfig(currentApiConfigName, trimmedValue) } resetRenameState() } const handleNewProfileSave = () => { const trimmedValue = newProfileName.trim() const error = validateName(trimmedValue, true) if (error) { setError(error) return } onUpsertConfig(trimmedValue) resetCreateState() } const handleDelete = () => { if (!currentApiConfigName || !listApiConfigMeta || listApiConfigMeta.length <= 1) return // Let the extension handle both deletion and selection onDeleteConfig(currentApiConfigName) } const isOnlyProfile = listApiConfigMeta?.length === 1 return (
{isRenaming ? (
{ const target = e as { target: { value: string } } setInputValue(target.target.value) setError(null) }} placeholder="Enter new name" style={{ flexGrow: 1 }} onKeyDown={(e: unknown) => { const event = e as { key: string } if (event.key === "Enter" && inputValue.trim()) { handleSave() } else if (event.key === "Escape") { handleCancel() } }} />
{error && (

{error}

)}
) : ( <>
{ onSelectConfig((value as DropdownOption).value) }} style={{ minWidth: 130, zIndex: 1002, }} role="combobox" options={listApiConfigMeta.map((config) => ({ value: config.name, label: config.name, }))} /> {currentApiConfigName && ( <> )}

Save different API configurations to quickly switch between providers and settings

)} { if (open) { setIsCreating(true) setNewProfileName("") setError(null) } else { resetCreateState() } }} aria-labelledby="new-profile-title"> New Configuration Profile { const target = e as { target: { value: string } } setNewProfileName(target.target.value) setError(null) }} placeholder="Enter profile name" style={{ width: "100%" }} onKeyDown={(e: unknown) => { const event = e as { key: string } if (event.key === "Enter" && newProfileName.trim()) { handleNewProfileSave() } else if (event.key === "Escape") { resetCreateState() } }} /> {error && (

{error}

)}
) } export default memo(ApiConfigManager)