From 7d9b20721e8b577d3b0e0cab051757ebdf5bf659 Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 21 Feb 2026 10:38:25 +0800 Subject: [PATCH] refactor(settings): split Advanced tab into Proxy tab and move Pricing to Usage Extract proxy-related accordion items (Local Proxy, Failover, Rectifier, Global Outbound Proxy) into a dedicated Proxy tab via ProxyTabContent component. Move Pricing config panel to UsageDashboard as a collapsible accordion. This reduces SettingsPage from ~716 to ~426 lines and improves settings discoverability with a 5-tab layout: General | Proxy | Advanced | Usage | About. --- src/components/settings/ProxyTabContent.tsx | 274 +++++++++++++++++ src/components/settings/SettingsPage.tsx | 314 +------------------- src/components/usage/UsageDashboard.tsx | 40 ++- src/i18n/locales/en.json | 1 + src/i18n/locales/ja.json | 1 + src/i18n/locales/zh.json | 1 + 6 files changed, 328 insertions(+), 303 deletions(-) create mode 100644 src/components/settings/ProxyTabContent.tsx diff --git a/src/components/settings/ProxyTabContent.tsx b/src/components/settings/ProxyTabContent.tsx new file mode 100644 index 000000000..9515aa0b5 --- /dev/null +++ b/src/components/settings/ProxyTabContent.tsx @@ -0,0 +1,274 @@ +import * as AccordionPrimitive from "@radix-ui/react-accordion"; +import { Server, Activity, ChevronDown, Zap, Globe } from "lucide-react"; +import { motion } from "framer-motion"; +import { useTranslation } from "react-i18next"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Switch } from "@/components/ui/switch"; +import { ToggleRow } from "@/components/ui/toggle-row"; +import { Badge } from "@/components/ui/badge"; +import { ProxyPanel } from "@/components/proxy"; +import { AutoFailoverConfigPanel } from "@/components/proxy/AutoFailoverConfigPanel"; +import { FailoverQueueManager } from "@/components/proxy/FailoverQueueManager"; +import { RectifierConfigPanel } from "@/components/settings/RectifierConfigPanel"; +import { GlobalProxySettings } from "@/components/settings/GlobalProxySettings"; +import { useProxyStatus } from "@/hooks/useProxyStatus"; +import type { SettingsFormState } from "@/hooks/useSettings"; + +interface ProxyTabContentProps { + settings: SettingsFormState; + onAutoSave: (updates: Partial) => Promise; +} + +export function ProxyTabContent({ + settings, + onAutoSave, +}: ProxyTabContentProps) { + const { t } = useTranslation(); + + const { + isRunning, + startProxyServer, + stopWithRestore, + isPending: isProxyPending, + } = useProxyStatus(); + + const handleToggleProxy = async (checked: boolean) => { + try { + if (!checked) { + await stopWithRestore(); + } else { + await startProxyServer(); + } + } catch (error) { + console.error("Toggle proxy failed:", error); + } + }; + + return ( + + + {/* Local Proxy */} + + + +
+ +
+

+ {t("settings.advanced.proxy.title")} +

+

+ {t("settings.advanced.proxy.description")} +

+
+
+ +
+ +
+ + + {isRunning + ? t("settings.advanced.proxy.running") + : t("settings.advanced.proxy.stopped")} + + +
+
+ + } + title={t("settings.advanced.proxy.enableFeature")} + description={t( + "settings.advanced.proxy.enableFeatureDescription", + )} + checked={settings?.enableLocalProxy ?? false} + onCheckedChange={(checked) => + onAutoSave({ enableLocalProxy: checked }) + } + /> +
+ +
+
+
+ + {/* Auto Failover */} + + +
+ +
+

+ {t("settings.advanced.failover.title")} +

+

+ {t("settings.advanced.failover.description")} +

+
+
+
+ +
+ {!isRunning && ( +
+

+ {t("proxy.failover.proxyRequired", { + defaultValue: "需要先启动代理服务才能配置故障转移", + })} +

+
+ )} + + + + Claude + Codex + Gemini + + +
+
+

+ {t("proxy.failoverQueue.title")} +

+

+ {t("proxy.failoverQueue.description")} +

+
+ +
+
+ +
+
+ +
+
+

+ {t("proxy.failoverQueue.title")} +

+

+ {t("proxy.failoverQueue.description")} +

+
+ +
+
+ +
+
+ +
+
+

+ {t("proxy.failoverQueue.title")} +

+

+ {t("proxy.failoverQueue.description")} +

+
+ +
+
+ +
+
+
+
+
+
+ + {/* Rectifier */} + + +
+ +
+

+ {t("settings.advanced.rectifier.title")} +

+

+ {t("settings.advanced.rectifier.description")} +

+
+
+
+ + + +
+ + {/* Global Outbound Proxy */} + + +
+ +
+

+ {t("settings.advanced.globalProxy.title")} +

+

+ {t("settings.advanced.globalProxy.description")} +

+
+
+
+ + + +
+
+
+ ); +} diff --git a/src/components/settings/SettingsPage.tsx b/src/components/settings/SettingsPage.tsx index d551c7796..1c493fef7 100644 --- a/src/components/settings/SettingsPage.tsx +++ b/src/components/settings/SettingsPage.tsx @@ -4,16 +4,9 @@ import { Loader2, Save, FolderSearch, - Activity, - Coins, Database, - Server, - ChevronDown, - Zap, - Globe, ScrollText, } from "lucide-react"; -import * as AccordionPrimitive from "@radix-ui/react-accordion"; import { toast } from "sonner"; import { Dialog, @@ -41,24 +34,15 @@ import { DirectorySettings } from "@/components/settings/DirectorySettings"; import { ImportExportSection } from "@/components/settings/ImportExportSection"; import { WebdavSyncSection } from "@/components/settings/WebdavSyncSection"; import { AboutSection } from "@/components/settings/AboutSection"; -import { GlobalProxySettings } from "@/components/settings/GlobalProxySettings"; -import { ProxyPanel } from "@/components/proxy"; -import { PricingConfigPanel } from "@/components/usage/PricingConfigPanel"; +import { ProxyTabContent } from "@/components/settings/ProxyTabContent"; // Hidden: stream check feature disabled // import { ModelTestConfigPanel } from "@/components/usage/ModelTestConfigPanel"; -import { AutoFailoverConfigPanel } from "@/components/proxy/AutoFailoverConfigPanel"; -import { FailoverQueueManager } from "@/components/proxy/FailoverQueueManager"; import { UsageDashboard } from "@/components/usage/UsageDashboard"; -import { RectifierConfigPanel } from "@/components/settings/RectifierConfigPanel"; import { LogConfigPanel } from "@/components/settings/LogConfigPanel"; import { useSettings } from "@/hooks/useSettings"; import { useImportExport } from "@/hooks/useImportExport"; import { useTranslation } from "react-i18next"; import type { SettingsFormState } from "@/hooks/useSettings"; -import { Switch } from "@/components/ui/switch"; -import { ToggleRow } from "@/components/ui/toggle-row"; -import { Badge } from "@/components/ui/badge"; -import { useProxyStatus } from "@/hooks/useProxyStatus"; interface SettingsDialogProps { open: boolean; @@ -190,25 +174,6 @@ export function SettingsPage({ const isBusy = useMemo(() => isLoading && !settings, [isLoading, settings]); - const { - isRunning, - startProxyServer, - stopWithRestore, - isPending: isProxyPending, - } = useProxyStatus(); - - const handleToggleProxy = async (checked: boolean) => { - try { - if (!checked) { - await stopWithRestore(); - } else { - await startProxyServer(); - } - } catch (error) { - console.error("Toggle proxy failed:", error); - } - }; - return (
{isBusy ? ( @@ -221,10 +186,11 @@ export function SettingsPage({ onValueChange={setActiveTab} className="flex flex-col h-full" > - + {t("settings.tabGeneral")} + {t("settings.tabProxy")} {t("settings.tabAdvanced")} @@ -271,6 +237,15 @@ export function SettingsPage({ ) : null} + + {settings ? ( + + ) : null} + + {settings ? ( - - - -
- -
-

- {t("settings.advanced.proxy.title")} -

-

- {t("settings.advanced.proxy.description")} -

-
-
- -
- -
- - - {isRunning - ? t("settings.advanced.proxy.running") - : t("settings.advanced.proxy.stopped")} - - -
-
- - } - title={t("settings.advanced.proxy.enableFeature")} - description={t( - "settings.advanced.proxy.enableFeatureDescription", - )} - checked={settings?.enableLocalProxy ?? false} - onCheckedChange={(checked) => - handleAutoSave({ enableLocalProxy: checked }) - } - /> -
- -
-
-
- - - -
- -
-

- {t("settings.advanced.failover.title")} -

-

- {t("settings.advanced.failover.description")} -

-
-
-
- -
- {/* 代理未运行时的提示 */} - {!isRunning && ( -
-

- {t("proxy.failover.proxyRequired", { - defaultValue: - "需要先启动代理服务才能配置故障转移", - })} -

-
- )} - - {/* 故障转移设置 - 按应用分组 */} - - - Claude - Codex - Gemini - - -
-
-

- {t("proxy.failoverQueue.title")} -

-

- {t("proxy.failoverQueue.description")} -

-
- -
-
- -
-
- -
-
-

- {t("proxy.failoverQueue.title")} -

-

- {t("proxy.failoverQueue.description")} -

-
- -
-
- -
-
- -
-
-

- {t("proxy.failoverQueue.title")} -

-

- {t("proxy.failoverQueue.description")} -

-
- -
-
- -
-
-
-
-
-
- - - -
- -
-

- {t("settings.advanced.rectifier.title")} -

-

- {t("settings.advanced.rectifier.description")} -

-
-
-
- - - -
- - {/* Hidden: stream check feature disabled - - -
- -
-

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

-

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

-
-
-
- - - -
- */} - - - -
- -
-

- {t("settings.advanced.pricing.title")} -

-

- {t("settings.advanced.pricing.description")} -

-
-
-
- - - -
- - - -
- -
-

- {t("settings.advanced.globalProxy.title")} -

-

- {t("settings.advanced.globalProxy.description")} -

-
-
-
- - - -
-
+ + {/* Pricing Configuration */} + + + +
+ +
+

+ {t("settings.advanced.pricing.title")} +

+

+ {t("settings.advanced.pricing.description")} +

+
+
+
+ + + +
+
); } diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index e79bc8954..7bc80a7ae 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -187,6 +187,7 @@ "general": "General", "tabGeneral": "General", "tabAdvanced": "Advanced", + "tabProxy": "Proxy", "advanced": { "configDir": { "title": "Configuration Directory", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index d09f49071..6aa3753ff 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -187,6 +187,7 @@ "general": "一般", "tabGeneral": "一般", "tabAdvanced": "詳細", + "tabProxy": "プロキシ", "advanced": { "configDir": { "title": "設定ディレクトリ", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index a56ba9a29..a94a0b484 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -187,6 +187,7 @@ "general": "通用", "tabGeneral": "通用", "tabAdvanced": "高级", + "tabProxy": "代理", "advanced": { "configDir": { "title": "配置文件目录",