import { useEffect, useState } from 'react' import { BrowserRouter, Routes, Route, Navigate, NavLink, Link, useLocation, useNavigate } from 'react-router-dom' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { Languages, Menu, MoreHorizontal, Moon, Sun } from 'lucide-react' import { buttonVariants } from '@/components/ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { AuthGate } from '@/components/auth-gate' import { I18nProvider, useI18n, SUPPORTED_LOCALES, type Locale } from '@/i18n' import { logout } from '@/lib/api' import KeysPage from '@/pages/KeysPage' import PlaygroundPage from '@/pages/PlaygroundPage' import FallbackPage from '@/pages/FallbackPage' import ModelDetailPage from '@/pages/ModelDetailPage' import FusionPage from '@/pages/FusionPage' import EmbeddingsPage from '@/pages/EmbeddingsPage' import ImagePage from '@/pages/ImagePage' import AudioPage from '@/pages/AudioPage' import MediaDetailPage from '@/pages/MediaDetailPage' import EmbeddingDetailPage from '@/pages/EmbeddingDetailPage' import AnalyticsPage from '@/pages/AnalyticsPage' import PremiumPage from '@/pages/PremiumPage' const queryClient = new QueryClient() const navItems = [ { to: '/models', labelKey: 'nav.models' }, { to: '/playground', labelKey: 'nav.playground' }, { to: '/keys', labelKey: 'nav.keys' }, { to: '/analytics', labelKey: 'nav.analytics' }, { to: '/premium', labelKey: 'nav.premium' }, ] function getPreferredDarkMode() { if (typeof window === 'undefined') { return false } const stored = localStorage.getItem('theme') return stored === 'dark' || (!stored && window.matchMedia('(prefers-color-scheme: dark)').matches) } function NavItem({ to, children }: { to: string; children: React.ReactNode }) { return ( `relative text-sm px-1 py-4 transition-colors ${ isActive ? 'text-foreground after:absolute after:inset-x-0 after:-bottom-px after:h-px after:bg-foreground' : 'text-muted-foreground hover:text-foreground' }` } > {children} ) } function useDarkMode() { const [dark, setDark] = useState(getPreferredDarkMode) useEffect(() => { document.documentElement.classList.toggle('dark', dark) }, [dark]) function toggle() { setDark((current) => { const next = !current localStorage.setItem('theme', next ? 'dark' : 'light') return next }) } return { dark, toggle } } function Brand() { return ( FreeLLMAPI ) } // True when the dashboard runs inside the desktop shell (Electron preload // sets this). The navbar then doubles as the window title bar: draggable, // padded for the macOS traffic lights, and without the web-only Sign out. const isDesktopApp = typeof window !== 'undefined' && (window as any).__FREEAPI_DESKTOP__ === true // The preload's own early classList.add can be lost (it may run before this // document exists), so the client claims the class itself at module load — // before the first React paint — keeping html.desktop CSS (transparent body, // glass backdrop) reliable. if (isDesktopApp) { document.documentElement.classList.add('desktop') } // Language picker as a dropdown submenu, shared by the desktop (⋯) and mobile // (☰) menus. Radio items show a check on the active locale; selecting one calls // setLocale, which persists and re-renders every t() synchronously. function LanguageSubMenu() { const { locale, setLocale, t } = useI18n() return ( {t('nav.language')} setLocale(v as Locale)}> {SUPPORTED_LOCALES.map((code) => ( {t(`languages.${code}`)} ))} ) } function Navbar() { const { dark, toggle } = useDarkMode() const { t } = useI18n() const location = useLocation() const navigate = useNavigate() function isActiveRoute(to: string) { return location.pathname === to } return (
{t('nav.theme')} {dark ? : } {!isDesktopApp && ( <> logout()}>{t('nav.signOut')} )}
{navItems.map((item) => ( navigate(item.to)} className={isActiveRoute(item.to) ? 'bg-accent text-accent-foreground font-medium' : undefined} > {t(item.labelKey)} ))} {t('nav.theme')} {dark ? : } {!isDesktopApp && ( logout()}>{t('nav.signOut')} )}
) } function App() { return (
} /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } />
) } export default App