import { useEffect, useRef, useState } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { listen } from '@tauri-apps/api/event'; import { Bot, Check, ChevronUp, ExternalLink, History, House, Languages, LogIn, MessageCircle, Network, PackageOpen, ServerCog, Settings, X, } 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 { KernelPage } from './pages/Kernel'; import { OAuthManagementPage } from './pages/ManagementPages'; import { AgentsPage } from './pages/AgentsPage'; import { UsageRecordsPage } from './pages/UsageRecordsPage'; import { languageOptions, useI18n } from './i18n'; import { AppUpdateDialog, AppUpdateProvider, useAppUpdate } from './appUpdate'; import { appUpdateIndicatorState } from './appUpdateModel'; import { canOpenAppPage, isAlwaysAvailablePage } from './navigation'; import { detectInitialTheme, saveTheme, type AppTheme } from './theme'; const CONTACT_URL = 'https://qm.qq.com/q/3queDaIG'; const pages = [ { id: 'home', labelKey: 'app.nav.home', icon: House, component: HomePage, }, { id: 'versions', labelKey: 'app.nav.versions', icon: PackageOpen, component: VersionManagementPage, }, { id: 'config', labelKey: 'app.nav.config', icon: Settings, component: ConfigPanelPage, }, { id: 'oauth', labelKey: 'app.nav.oauth', icon: LogIn, component: OAuthManagementPage, }, { id: 'api', labelKey: 'app.nav.api', icon: Network, component: ApiAccessPage, }, { id: 'usage-records', labelKey: 'app.nav.usageRecords', icon: History, component: UsageRecordsPage, }, { id: 'agents', labelKey: 'app.nav.agents', 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 HomePage() { return ; } function VersionManagementPage() { return ; } function App() { return ( ); } function AppContent() { const { locale, setLocale, t } = useI18n(); const { info: appUpdateInfo, hasUpdate, processing: appUpdateProcessing } = useAppUpdate(); const [active, setActive] = useState('home'); const [languageMenuOpen, setLanguageMenuOpen] = useState(false); const [theme, setTheme] = useState(detectInitialTheme); const [windowsClosePrompt, setWindowsClosePrompt] = useState(null); const closeDialogRef = useRef(null); const languageMenuRef = useRef(null); const languageButtonRef = useRef(null); const { status } = useCoreRuntime(); const coreRunning = Boolean(status?.running); const activePage = pages.find((page) => page.id === active) ?? pages[0]; const ActivePage = activePage.component; const selectedLanguage = languageOptions.find((option) => option.value === locale) ?? languageOptions[0]; useEffect(() => { saveTheme(theme); }, [theme]); useEffect(() => { if (!canOpenAppPage(active, coreRunning)) { setActive('home'); } }, [active, coreRunning]); useEffect(() => { if (!languageMenuOpen) return undefined; const closeFromOutside = (event: PointerEvent) => { if (!languageMenuRef.current?.contains(event.target as Node)) { setLanguageMenuOpen(false); } }; const closeFromKeyboard = (event: KeyboardEvent) => { if (event.key !== 'Escape') return; setLanguageMenuOpen(false); languageButtonRef.current?.focus(); }; document.addEventListener('pointerdown', closeFromOutside); document.addEventListener('keydown', closeFromKeyboard); return () => { document.removeEventListener('pointerdown', closeFromOutside); document.removeEventListener('keydown', closeFromKeyboard); }; }, [languageMenuOpen]); 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 (!canOpenAppPage(pageId, 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 ( <>
{isAlwaysAvailablePage(activePage.id) || coreRunning ? ( ) : ( )}
{windowsClosePrompt ? (
{ if (event.key === 'Escape') { event.preventDefault(); } }} >

{t('app.close.title')}

{t('app.close.description')}

{windowsClosePrompt.error ? (
{windowsClosePrompt.error}
) : null}
) : null} ); } function CoreLockedPage() { const { t } = useI18n(); return (
); } export default App;