import { useCallback, useEffect, useMemo, useState } from "react"; import { motion } from "framer-motion"; import { Loader2, Save, FolderSearch, Database, Cloud, ScrollText, HardDriveDownload, FlaskConical, KeyRound, } from "lucide-react"; import { toast } from "sonner"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Button } from "@/components/ui/button"; import { settingsApi } from "@/lib/api"; import { LanguageSettings } from "@/components/settings/LanguageSettings"; import { ThemeSettings } from "@/components/settings/ThemeSettings"; import { WindowSettings } from "@/components/settings/WindowSettings"; import { AppVisibilitySettings } from "@/components/settings/AppVisibilitySettings"; import { SkillSyncMethodSettings } from "@/components/settings/SkillSyncMethodSettings"; import { TerminalSettings } from "@/components/settings/TerminalSettings"; import { DirectorySettings } from "@/components/settings/DirectorySettings"; import { ImportExportSection } from "@/components/settings/ImportExportSection"; import { BackupListSection } from "@/components/settings/BackupListSection"; import { WebdavSyncSection } from "@/components/settings/WebdavSyncSection"; import { AboutSection } from "@/components/settings/AboutSection"; import { ProxyTabContent } from "@/components/settings/ProxyTabContent"; import { ModelTestConfigPanel } from "@/components/usage/ModelTestConfigPanel"; import { UsageDashboard } from "@/components/usage/UsageDashboard"; import { LogConfigPanel } from "@/components/settings/LogConfigPanel"; import { AuthCenterPanel } from "@/components/settings/AuthCenterPanel"; import { useSettings } from "@/hooks/useSettings"; import { useImportExport } from "@/hooks/useImportExport"; import { useTranslation } from "react-i18next"; import type { SettingsFormState } from "@/hooks/useSettings"; interface SettingsDialogProps { open: boolean; onOpenChange: (open: boolean) => void; onImportSuccess?: () => void | Promise; defaultTab?: string; } export function SettingsPage({ open, onOpenChange, onImportSuccess, defaultTab = "general", }: SettingsDialogProps) { const { t } = useTranslation(); const { settings, isLoading, isSaving, isPortable, appConfigDir, resolvedDirs, updateSettings, updateDirectory, updateAppConfigDir, browseDirectory, browseAppConfigDir, resetDirectory, resetAppConfigDir, saveSettings, autoSaveSettings, requiresRestart, acknowledgeRestart, } = useSettings(); const { selectedFile, status: importStatus, errorMessage, backupId, isImporting, selectImportFile, importConfig, exportConfig, clearSelection, resetStatus, } = useImportExport({ onImportSuccess }); const [activeTab, setActiveTab] = useState("general"); const [showRestartPrompt, setShowRestartPrompt] = useState(false); useEffect(() => { if (open) { setActiveTab(defaultTab); resetStatus(); } }, [open, resetStatus, defaultTab]); useEffect(() => { if (requiresRestart) { setShowRestartPrompt(true); } }, [requiresRestart]); const closeAfterSave = useCallback(() => { // 保存成功后关闭:不再重置语言,避免需要“保存两次”才生效 acknowledgeRestart(); clearSelection(); resetStatus(); onOpenChange(false); }, [acknowledgeRestart, clearSelection, onOpenChange, resetStatus]); const handleSave = useCallback(async () => { try { const result = await saveSettings(undefined, { silent: false }); if (!result) return; if (result.requiresRestart) { setShowRestartPrompt(true); return; } closeAfterSave(); } catch (error) { console.error("[SettingsPage] Failed to save settings", error); } }, [closeAfterSave, saveSettings]); const handleRestartLater = useCallback(() => { setShowRestartPrompt(false); closeAfterSave(); }, [closeAfterSave]); const handleRestartNow = useCallback(async () => { setShowRestartPrompt(false); if (import.meta.env.DEV) { toast.success(t("settings.devModeRestartHint"), { closeButton: true }); closeAfterSave(); return; } try { await settingsApi.restart(); } catch (error) { console.error("[SettingsPage] Failed to restart app", error); toast.error(t("settings.restartFailed")); } finally { closeAfterSave(); } }, [closeAfterSave, t]); // 通用设置即时保存(无需手动点击) // 使用 autoSaveSettings 避免误触发系统 API(开机自启、Claude 插件等) const handleAutoSave = useCallback( async (updates: Partial) => { if (!settings) return; updateSettings(updates); try { await autoSaveSettings(updates); } catch (error) { console.error("[SettingsPage] Failed to autosave settings", error); toast.error( t("settings.saveFailedGeneric", { defaultValue: "保存失败,请重试", }), ); } }, [autoSaveSettings, settings, t, updateSettings], ); const isBusy = useMemo(() => isLoading && !settings, [isLoading, settings]); return (
{isBusy ? (
) : ( {t("settings.tabGeneral")} {t("settings.tabProxy")} {t("settings.tabAuth", { defaultValue: "认证" })} {t("settings.tabAdvanced")} {t("usage.title")} {t("common.about")}
{settings ? ( handleAutoSave({ language: lang })} /> handleAutoSave({ skillSyncMethod: method }) } /> handleAutoSave({ preferredTerminal: terminal }) } /> ) : null} {settings ? ( ) : null}

{t("settings.authCenter.heading", { defaultValue: "认证中心", })}

{t("settings.authCenter.headingDescription", { defaultValue: "统一管理可跨应用复用的 OAuth 账号和默认认证来源。", })}

{settings ? (

{t("settings.advanced.configDir.title")}

{t("settings.advanced.configDir.description")}

{t("settings.advanced.data.title")}

{t("settings.advanced.data.description")}

{t("settings.advanced.backup.title", { defaultValue: "Backup & Restore", })}

{t("settings.advanced.backup.description", { defaultValue: "Manage automatic backups, view and restore database snapshots", })}

handleAutoSave(updates) } />

{t("settings.advanced.cloudSync.title")}

{t("settings.advanced.cloudSync.description")}

{t("settings.advanced.modelTest.title")}

{t("settings.advanced.modelTest.description")}

{t("settings.advanced.logConfig.title")}

{t("settings.advanced.logConfig.description")}

) : null}
{activeTab === "advanced" && settings && (
)}
)} !open && handleRestartLater()} > {t("settings.restartRequired")}

{t("settings.restartRequiredMessage")}

); }