import { useState, useEffect, useRef } from 'react' import { Provider } from '../shared/types' import ProviderList from './components/ProviderList' import AddProviderModal from './components/AddProviderModal' import EditProviderModal from './components/EditProviderModal' import { ConfirmDialog } from './components/ConfirmDialog' import './App.css' function App() { const [providers, setProviders] = useState>({}) const [currentProviderId, setCurrentProviderId] = useState('') const [isAddModalOpen, setIsAddModalOpen] = useState(false) const [configPath, setConfigPath] = useState('') const [editingProviderId, setEditingProviderId] = useState(null) const [notification, setNotification] = useState<{ message: string; type: 'success' | 'error' } | null>(null) const [isNotificationVisible, setIsNotificationVisible] = useState(false) const [confirmDialog, setConfirmDialog] = useState<{ isOpen: boolean; title: string; message: string; onConfirm: () => void } | null>(null) const timeoutRef = useRef(null) // 设置通知的辅助函数 const showNotification = (message: string, type: 'success' | 'error', duration = 3000) => { // 清除之前的定时器 if (timeoutRef.current) { clearTimeout(timeoutRef.current) } // 立即显示通知 setNotification({ message, type }) setIsNotificationVisible(true) // 设置淡出定时器 timeoutRef.current = setTimeout(() => { setIsNotificationVisible(false) // 等待淡出动画完成后清除通知 setTimeout(() => { setNotification(null) timeoutRef.current = null }, 300) // 与CSS动画时间匹配 }, duration) } // 加载供应商列表 useEffect(() => { loadProviders() loadConfigPath() }, []) // 清理定时器 useEffect(() => { return () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current) } } }, []) const loadProviders = async () => { const loadedProviders = await window.electronAPI.getProviders() const currentId = await window.electronAPI.getCurrentProvider() setProviders(loadedProviders) setCurrentProviderId(currentId) } const loadConfigPath = async () => { const path = await window.electronAPI.getClaudeCodeConfigPath() setConfigPath(path) } // 生成唯一ID const generateId = () => { return Date.now().toString(36) + Math.random().toString(36).substr(2, 9) } const handleAddProvider = async (provider: Omit) => { const newProvider: Provider = { ...provider, id: generateId() } await window.electronAPI.addProvider(newProvider) await loadProviders() setIsAddModalOpen(false) } const handleDeleteProvider = async (id: string) => { const provider = providers[id] setConfirmDialog({ isOpen: true, title: '删除供应商', message: `确定要删除供应商 "${provider?.name}" 吗?此操作无法撤销。`, onConfirm: async () => { await window.electronAPI.deleteProvider(id) await loadProviders() setConfirmDialog(null) showNotification('供应商删除成功', 'success') } }) } const handleSwitchProvider = async (id: string) => { const success = await window.electronAPI.switchProvider(id) if (success) { setCurrentProviderId(id) // 显示重启提示,时间更长 showNotification('切换成功!请重启 Claude Code 终端以生效', 'success', 4000) } else { showNotification('切换失败,请检查配置', 'error') } } const handleEditProvider = async (provider: Provider) => { try { await window.electronAPI.updateProvider(provider) await loadProviders() setEditingProviderId(null) // 显示编辑成功提示,时间较短 showNotification('供应商配置已保存', 'success', 2000) } catch (error) { console.error('更新供应商失败:', error) setEditingProviderId(null) showNotification('保存失败,请重试', 'error') } } const handleSelectConfigFile = async () => { const selectedPath = await window.electronAPI.selectConfigFile() if (selectedPath) { setConfigPath(selectedPath) } } return (

Claude Code 供应商切换器

{/* 浮动通知组件 */} {notification && (
{notification.message}
)}
{configPath && (
配置文件位置: {configPath}
)}
{isAddModalOpen && ( setIsAddModalOpen(false)} /> )} {editingProviderId && providers[editingProviderId] && ( setEditingProviderId(null)} /> )} {confirmDialog && ( setConfirmDialog(null)} /> )}
) } export default App