import { useState, useEffect, useRef } from "react"; import { Provider } from "./types"; import { AppType } from "./lib/tauri-api"; import ProviderList from "./components/ProviderList"; import AddProviderModal from "./components/AddProviderModal"; import EditProviderModal from "./components/EditProviderModal"; import { ConfirmDialog } from "./components/ConfirmDialog"; import { AppSwitcher } from "./components/AppSwitcher"; import SettingsModal from "./components/SettingsModal"; import { UpdateBadge } from "./components/UpdateBadge"; import { Plus, Settings, Moon, Sun } from "lucide-react"; import { buttonStyles } from "./lib/styles"; import { useDarkMode } from "./hooks/useDarkMode"; function App() { const { isDarkMode, toggleDarkMode } = useDarkMode(); const [activeApp, setActiveApp] = useState("claude"); const [providers, setProviders] = useState>({}); const [currentProviderId, setCurrentProviderId] = useState(""); const [isAddModalOpen, setIsAddModalOpen] = useState(false); 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 [isSettingsOpen, setIsSettingsOpen] = useState(false); const timeoutRef = useRef | null>(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(); }, [activeApp]); // 当切换应用时重新加载 // 清理定时器 useEffect(() => { return () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } }; }, []); // 监听托盘切换事件 useEffect(() => { let unlisten: (() => void) | null = null; const setupListener = async () => { try { unlisten = await window.api.onProviderSwitched(async (data) => { if (import.meta.env.DEV) { console.log("收到供应商切换事件:", data); } // 如果当前应用类型匹配,则重新加载数据 if (data.appType === activeApp) { await loadProviders(); } }); } catch (error) { console.error("设置供应商切换监听器失败:", error); } }; setupListener(); // 清理监听器 return () => { if (unlisten) { unlisten(); } }; }, [activeApp]); // 依赖activeApp,切换应用时重新设置监听器 const loadProviders = async () => { const loadedProviders = await window.api.getProviders(activeApp); const currentId = await window.api.getCurrentProvider(activeApp); setProviders(loadedProviders); setCurrentProviderId(currentId); // 如果供应商列表为空,尝试自动从 live 导入一条默认供应商 if (Object.keys(loadedProviders).length === 0) { await handleAutoImportDefault(); } }; // 生成唯一ID const generateId = () => { return crypto.randomUUID(); }; const handleAddProvider = async (provider: Omit) => { const newProvider: Provider = { ...provider, id: generateId(), createdAt: Date.now(), // 添加创建时间戳 }; await window.api.addProvider(newProvider, activeApp); await loadProviders(); setIsAddModalOpen(false); // 更新托盘菜单 await window.api.updateTrayMenu(); }; const handleEditProvider = async (provider: Provider) => { try { await window.api.updateProvider(provider, activeApp); await loadProviders(); setEditingProviderId(null); // 显示编辑成功提示 showNotification("供应商配置已保存", "success", 2000); // 更新托盘菜单 await window.api.updateTrayMenu(); } catch (error) { console.error("更新供应商失败:", error); setEditingProviderId(null); showNotification("保存失败,请重试", "error"); } }; const handleDeleteProvider = async (id: string) => { const provider = providers[id]; setConfirmDialog({ isOpen: true, title: "删除供应商", message: `确定要删除供应商 "${provider?.name}" 吗?此操作无法撤销。`, onConfirm: async () => { await window.api.deleteProvider(id, activeApp); await loadProviders(); setConfirmDialog(null); showNotification("供应商删除成功", "success"); // 更新托盘菜单 await window.api.updateTrayMenu(); }, }); }; const handleSwitchProvider = async (id: string) => { const success = await window.api.switchProvider(id, activeApp); if (success) { setCurrentProviderId(id); // 显示重启提示 const appName = activeApp === "claude" ? "Claude Code" : "Codex"; showNotification( `切换成功!请重启 ${appName} 终端以生效`, "success", 2000, ); // 更新托盘菜单 await window.api.updateTrayMenu(); } else { showNotification("切换失败,请检查配置", "error"); } }; // 自动从 live 导入一条默认供应商(仅首次初始化时) const handleAutoImportDefault = async () => { try { const result = await window.api.importCurrentConfigAsDefault(activeApp); if (result.success) { await loadProviders(); showNotification("已从现有配置创建默认供应商", "success", 3000); // 更新托盘菜单 await window.api.updateTrayMenu(); } // 如果导入失败(比如没有现有配置),静默处理,不显示错误 } catch (error) { console.error("自动导入默认配置失败:", error); // 静默处理,不影响用户体验 } }; return (
{/* 顶部导航区域 - 固定高度 */}

CC Switch

setIsSettingsOpen(true)} />
{/* 主内容区域 - 独立滚动 */}
{/* 通知组件 - 相对于视窗定位 */} {notification && (
{notification.message}
)}
{isAddModalOpen && ( setIsAddModalOpen(false)} /> )} {editingProviderId && providers[editingProviderId] && ( setEditingProviderId(null)} /> )} {confirmDialog && ( setConfirmDialog(null)} /> )} {isSettingsOpen && ( setIsSettingsOpen(false)} /> )}
); } export default App;