import { useEffect, useRef, useState } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { listen } from '@tauri-apps/api/event'; import { Bot, ExternalLink, FileKey, Gauge, GitFork, History, LogIn, MessageCircle, Network, ServerCog, Settings, } from 'lucide-react'; import appLogo from './assets/logo.jpg'; import { CoreRuntimeProvider, useCoreRuntime } from './coreRuntime'; import { ConfigPanelPage } from './pages/ConfigPanel'; import { ApiAccessPage } from './pages/ApiAccessPage'; import { AuthFileManagementPage } from './pages/AuthFileManagementPage'; import { KernelPage } from './pages/Kernel'; import { OAuthLoginPage } from './pages/ManagementPages'; import { QuotaPage } from './pages/QuotaPage'; import { AgentsPage } from './pages/AgentsPage'; import { ThinkingAliasesPage } from './pages/ThinkingAliasesPage'; import { UsageRecordsPage } from './pages/UsageRecordsPage'; const CONTACT_URL = 'https://qm.qq.com/q/3queDaIG'; const pages = [ { id: 'kernel', label: '内核', icon: ServerCog, component: KernelPage, }, { id: 'config', label: '配置', icon: Settings, component: ConfigPanelPage, }, { id: 'thinking-aliases', label: '模型别名', icon: GitFork, component: ThinkingAliasesPage, }, { id: 'oauth', label: 'OAuth', icon: LogIn, component: OAuthLoginPage, }, { id: 'api', label: 'API 接入', icon: Network, component: ApiAccessPage, }, { id: 'auth-files', label: '认证文件', icon: FileKey, component: AuthFileManagementPage, }, { id: 'quota', label: '配额', icon: Gauge, component: QuotaPage, }, { id: 'usage-records', label: '使用记录', icon: History, component: UsageRecordsPage, }, { id: 'agents', label: '智能体', icon: Bot, component: AgentsPage, }, ] as const; type PageId = (typeof pages)[number]['id']; type WindowsCloseAction = 'exit' | 'minimize-to-tray'; type WindowsClosePrompt = { resolvingAction: WindowsCloseAction | null; error: string | null; }; function App() { return ( ); } function AppContent() { const [active, setActive] = useState('kernel'); const [windowsClosePrompt, setWindowsClosePrompt] = useState(null); const closeDialogRef = useRef(null); const { status } = useCoreRuntime(); const coreRunning = Boolean(status?.running); const activePage = pages.find((page) => page.id === active) ?? pages[0]; const ActivePage = activePage.component; useEffect(() => { if (!coreRunning && active !== 'kernel') { setActive('kernel'); } }, [active, coreRunning]); useEffect(() => { let disposed = false; let stopListening: (() => void) | undefined; void listen('windows-close-requested', () => { setWindowsClosePrompt((current) => current ?? { resolvingAction: null, error: null, }, ); }) .then((stop) => { if (disposed) { stop(); } else { stopListening = stop; } }) .catch((error) => { console.error('监听 Windows 关闭确认事件失败', error); }); return () => { disposed = true; stopListening?.(); }; }, []); useEffect(() => { if (!windowsClosePrompt || windowsClosePrompt.resolvingAction) { return; } const frame = window.requestAnimationFrame(() => { closeDialogRef.current?.focus(); }); return () => window.cancelAnimationFrame(frame); }, [windowsClosePrompt]); const select = (pageId: PageId) => { if (pageId !== 'kernel' && !coreRunning) { return; } setActive(pageId); }; const openContact = async () => { try { await invoke('open_external_url', { url: CONTACT_URL }); } catch (error) { console.error('打开联系我们链接失败', error); } }; const resolveWindowsCloseRequest = async (action: WindowsCloseAction) => { setWindowsClosePrompt((current) => current ? { ...current, resolvingAction: action, error: null, } : current, ); try { await invoke('resolve_windows_close_request', { action }); setWindowsClosePrompt(null); } catch (error) { setWindowsClosePrompt((current) => current ? { ...current, resolvingAction: null, error: error instanceof Error ? error.message : String(error), } : current, ); } }; return ( <> {activePage.id === 'kernel' || coreRunning ? ( ) : ( )} {windowsClosePrompt ? ( { if (event.key === 'Escape') { event.preventDefault(); } }} > 关闭窗口 请选择后续操作 你可以退出程序,或让 EasyCLIProxyAPI 继续在系统托盘中运行。 {windowsClosePrompt.error ? ( {windowsClosePrompt.error} ) : null} void resolveWindowsCloseRequest('minimize-to-tray')} > {windowsClosePrompt.resolvingAction === 'minimize-to-tray' ? '正在最小化...' : '最小化到托盘'} void resolveWindowsCloseRequest('exit')} > {windowsClosePrompt.resolvingAction === 'exit' ? '正在退出...' : '退出程序'} ) : null} > ); } function CoreLockedPage() { return ( 请先启动内核 启动 CPA 内核后,才能使用配置、OAuth、API 接入等功能。 ); } export default App;
你可以退出程序,或让 EasyCLIProxyAPI 继续在系统托盘中运行。