import { createFileRoute } from '@tanstack/react-router'; import { useTheme } from '@/components/theme-provider'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { Input } from '@/components/ui/input'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { useQuery } from '@tanstack/react-query'; import { getAppVersion, getPlatform } from '@/actions/app'; import { useTranslation } from 'react-i18next'; import { setAppLanguage } from '@/actions/language'; import { useAppConfig } from '@/hooks/useAppConfig'; import { useToast } from '@/components/ui/use-toast'; import { Loader2, FolderOpen } from 'lucide-react'; import { ModelVisibilitySettings } from '@/components/ModelVisibilitySettings'; import { useEffect, useState } from 'react'; import { ProxyConfig } from '@/types/config'; import { openLogDirectory } from '@/actions/system'; import { AccountProxySettings } from '@/components/AccountProxySettings'; function SettingsPage() { const { theme, setTheme } = useTheme(); const { t, i18n } = useTranslation(); const { config, isLoading, saveConfig } = useAppConfig(); const { toast } = useToast(); // Local state for configuration editing const [proxyConfig, setProxyConfig] = useState(undefined); // Sync config to local state when loaded useEffect(() => { if (config) { // eslint-disable-next-line setProxyConfig(config.proxy); } }, [config]); const { data: appVersion } = useQuery({ queryKey: ['app', 'version'], queryFn: getAppVersion, }); const { data: platform } = useQuery({ queryKey: ['app', 'platform'], queryFn: getPlatform, }); const isAutoStartSupported = platform === 'win32' || platform === 'darwin' || platform === 'linux'; const isMac = platform === 'darwin'; const handleLanguageChange = (value: string) => { setAppLanguage(value, i18n); }; // Helper to update proxyConfig and auto-save const updateProxyConfig = async (newProxyConfig: ProxyConfig) => { setProxyConfig(newProxyConfig); if (config) { await saveConfig({ ...config, proxy: newProxyConfig }); } }; if (isLoading || !proxyConfig) { return (
); } return (

{t('settings.title')}

{t('settings.description')}

{t('settings.general')} {t('settings.models')} {t('settings.proxy_tab')} {/* --- GENERAL TAB --- */} {t('settings.appearance.title')} {t('settings.appearance.description')}

{t('settings.darkModeDescription')}

setTheme(checked ? 'dark' : 'light')} />

{t('settings.language.description')}

{/* Account Settings Card */} {t('settings.account.title')} {t('settings.account.description')} {/* Auto Refresh Quota */}

{t('settings.account.auto_refresh_desc')}

{ if (config) { await saveConfig({ ...config, auto_refresh: checked }); } }} />
{/* Auto Sync Account */}

{t('settings.account.auto_sync_desc')}

{ if (config) { await saveConfig({ ...config, auto_sync: checked }); } }} />
{isAutoStartSupported && ( {t('settings.startup.title')} {t('settings.startup.description')}

{t('settings.startup.auto_startup_desc')}

{ if (config) { await saveConfig({ ...config, auto_startup: checked }); } }} />
{isMac && (

{t('settings.startup.macos_hint')}

)}
)} {t('settings.about.title')} {t('settings.about.description')}
{t('settings.version')}
{appVersion || t('common.unknown')}
{t('settings.platform')}
{platform || t('common.unknown')}
{t('settings.license')}
CC BY-NC-SA 4.0
{t('action.openLogs')}
{/* Privacy & Error Reporting Card */} {t('settings.privacy.title')} {t('settings.privacy.description')}

{t('settings.privacy.error_reporting_desc')}

{ if (config) { await saveConfig({ ...config, error_reporting_enabled: checked }); } }} />

{t('settings.privacy.restart_note')}

{/* Notifications Card */} {t('settings.notifications.title')} {t('settings.notifications.description')}

{t('settings.notifications.quotaAlertDesc')}

{ if (config) { try { await saveConfig({ ...config, quota_alert_enabled: checked }); } catch { toast({ title: t('common.error'), description: t('settings.notifications.saveFailed'), variant: 'destructive', }); } } }} />

{t('settings.notifications.quotaThresholdDesc')}

{ const rawValue = e.target.value; const parsed = parseInt(rawValue, 10); if (isNaN(parsed) || parsed < 0 || parsed > 100) return; if (config) { try { await saveConfig({ ...config, quota_alert_threshold: parsed }); } catch { toast({ title: t('common.error'), description: t('settings.notifications.thresholdSaveFailed'), variant: 'destructive', }); } } }} className="bg-muted/50 text-foreground focus:border-accent/50 w-16 rounded-lg border border-white/[0.08] px-2 py-1 text-center text-[13px] focus:outline-none" /> %
{/* --- MODELS TAB --- */} {/* --- PROXY TAB (Upstream Proxy Config Only) --- */} {t('settings.proxy.title')} {t('settings.proxy.description')}
updateProxyConfig({ ...proxyConfig, upstream_proxy: { ...proxyConfig.upstream_proxy, enabled: checked }, }) } />
updateProxyConfig({ ...proxyConfig, upstream_proxy: { ...proxyConfig.upstream_proxy, url: e.target.value }, }) } disabled={!proxyConfig.upstream_proxy.enabled} />
); } export const Route = createFileRoute('/settings')({ component: SettingsPage, });