From 1de00d9c4f4decdd4e59028914a9941b1a5348fa Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Sat, 11 Oct 2025 01:24:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=BB=9A=E5=8A=A8=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 6 +- src/App.tsx | 62 ++++++---- src/components/RelayStationManager.tsx | 10 +- src/components/Settings.tsx | 150 +++++++++++++++++++++---- src/lib/api.ts | 36 ++++++ 5 files changed, 213 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index 377f760..6dcebb8 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,11 @@ "preview": "vite preview", "tauri": "tauri", "check": "tsc --noEmit && cd src-tauri && cargo check", - "postinstall": "node ./scripts/copy-monaco.mjs" + "postinstall": "node ./scripts/copy-monaco.mjs", + "create-todo": "bash ./scripts/create-todo.sh", + "archive-todo": "bash ./scripts/archive-todo.sh", + "todo-report": "bash ./scripts/todo-report.sh", + "pre-commit": "bash ./scripts/pre-commit-check.sh" }, "dependencies": { "@hookform/resolvers": "^3.9.1", diff --git a/src/App.tsx b/src/App.tsx index 98ebbda..76f93e5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -283,24 +283,30 @@ function AppContent() { case "relay-stations": return ( - handleViewChange("welcome")} /> +
+ handleViewChange("welcome")} /> +
); case "ccr-router": return ( - handleViewChange("welcome")} /> +
+ handleViewChange("welcome")} /> +
); case "cc-agents": return ( - handleViewChange("welcome")} - /> +
+ handleViewChange("welcome")} + /> +
); case "editor": return ( -
+
}> handleViewChange("welcome")} /> @@ -309,7 +315,7 @@ function AppContent() { case "settings": return ( -
+
}> handleViewChange("welcome")} /> @@ -447,10 +453,12 @@ function AppContent() { case "claude-file-editor": return editingClaudeFile ? ( - +
+ +
) : null; case "tabs": @@ -465,28 +473,34 @@ function AppContent() { case "usage-dashboard": return ( -
}> - handleViewChange("welcome")} /> - +
+
}> + handleViewChange("welcome")} /> + +
); case "mcp": return ( - }> - handleViewChange("welcome")} /> - +
+
}> + handleViewChange("welcome")} /> + + ); case "project-settings": if (projectForSettings) { return ( - { - setProjectForSettings(null); - handleViewChange(previousView || "projects"); - }} - /> +
+ { + setProjectForSettings(null); + handleViewChange(previousView || "projects"); + }} + /> +
); } break; diff --git a/src/components/RelayStationManager.tsx b/src/components/RelayStationManager.tsx index 45fb5be..61d3ea9 100644 --- a/src/components/RelayStationManager.tsx +++ b/src/components/RelayStationManager.tsx @@ -409,9 +409,11 @@ const RelayStationManager: React.FC = ({ onBack }) => }, [stations]); return ( -
- {/* 页面标题 */} -
+
+
+
+ {/* 页面标题 */} +
+
); }; diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 3269dd4..e8c7243 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -21,7 +21,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { api, type ClaudeSettings, - type ClaudeInstallation + type ClaudeInstallation, + type ModelMapping } from "@/lib/api"; import { cn } from "@/lib/utils"; import { Toast, ToastContainer } from "@/components/ui/toast"; @@ -99,11 +100,17 @@ export const Settings: React.FC = ({ const [showAnalyticsConsent, setShowAnalyticsConsent] = useState(false); const trackEvent = useTrackEvent(); + // Model mappings state + const [modelMappings, setModelMappings] = useState([]); + const [loadingMappings, setLoadingMappings] = useState(false); + const [modelMappingsChanged, setModelMappingsChanged] = useState(false); + // Load settings on mount useEffect(() => { loadSettings(); loadClaudeBinaryPath(); loadAnalyticsSettings(); + loadModelMappings(); }, []); /** @@ -117,6 +124,51 @@ export const Settings: React.FC = ({ } }; + /** + * Loads model mappings + * @author yovinchen + */ + const loadModelMappings = async () => { + try { + setLoadingMappings(true); + const mappings = await api.getModelMappings(); + setModelMappings(mappings); + } catch (err) { + console.error("Failed to load model mappings:", err); + setToast({ message: "加载模型映射失败", type: "error" }); + } finally { + setLoadingMappings(false); + } + }; + + /** + * Updates a model mapping + * @author yovinchen + */ + const updateModelMapping = (alias: string, modelName: string) => { + setModelMappings(prev => + prev.map(m => (m.alias === alias ? { ...m, model_name: modelName } : m)) + ); + setModelMappingsChanged(true); + }; + + /** + * Saves model mappings + * @author yovinchen + */ + const saveModelMappings = async () => { + try { + for (const mapping of modelMappings) { + await api.updateModelMapping(mapping.alias, mapping.model_name); + } + setModelMappingsChanged(false); + setToast({ message: "模型映射已保存", type: "success" }); + } catch (err) { + console.error("Failed to save model mappings:", err); + setToast({ message: "保存模型映射失败", type: "error" }); + } + }; + /** * Loads the current Claude binary path */ @@ -233,6 +285,11 @@ export const Settings: React.FC = ({ setProxySettingsChanged(false); } + // Save model mappings if changed + if (modelMappingsChanged) { + await saveModelMappings(); + } + setToast({ message: t('settings.saveButton.settingsSavedSuccess'), type: "success" }); } catch (err) { console.error("Failed to save settings:", err); @@ -396,22 +453,23 @@ export const Settings: React.FC = ({
) : ( -
- - - {t('settings.general')} - {t('settings.permissionsTab')} - {t('settings.environmentTab')} - {t('settings.advancedTab')} - {t('settings.hooksTab')} - {t('settings.commands')} - {t('settings.storage')} - {t('settings.proxy')} - {t('settings.analyticsTab')} - +
+
+ + + {t('settings.general')} + {t('settings.permissionsTab')} + {t('settings.environmentTab')} + {t('settings.advancedTab')} + {t('settings.hooksTab')} + {t('settings.commands')} + {t('settings.storage')} + {t('settings.proxy')} + {t('settings.analyticsTab')} + {/* General Settings */} - +

{t('settings.generalSettings')}

@@ -633,13 +691,58 @@ export const Settings: React.FC = ({

)}
+ + {/* Model Mappings Configuration */} +
+
+ +

+ 配置模型别名(sonnet、opus、haiku)对应的实际模型版本 +

+
+ + {loadingMappings ? ( +
+ +
+ ) : ( +
+ {modelMappings.map((mapping) => ( +
+ + updateModelMapping(mapping.alias, e.target.value)} + placeholder={`claude-${mapping.alias}-4-...`} + className="font-mono text-sm" + /> +
+ ))} + + {modelMappingsChanged && ( +

+ 模型映射已修改,点击保存以应用更改 +

+ )} + +
+

+ 说明:Agent执行时会根据这里的配置解析模型别名。例如,如果设置 sonnet → claude-sonnet-4-20250514,那么所有使用 "sonnet" 的Agent都会调用该模型版本。 +

+
+
+ )} +
{/* Permissions Settings */} - +
@@ -760,7 +863,7 @@ export const Settings: React.FC = ({ {/* Environment Variables */} - +
@@ -834,7 +937,7 @@ export const Settings: React.FC = ({ {/* Advanced Settings */} - +
@@ -873,7 +976,7 @@ export const Settings: React.FC = ({ {/* Hooks Settings */} - +
@@ -898,19 +1001,19 @@ export const Settings: React.FC = ({ {/* Commands Tab */} - + {/* Storage Tab */} - + {/* Proxy Settings */} - + = ({ {/* Analytics Settings */} - +
@@ -1013,6 +1116,7 @@ export const Settings: React.FC = ({ +
)}
diff --git a/src/lib/api.ts b/src/lib/api.ts index 04b9c19..8403d99 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -2698,6 +2698,32 @@ export const api = { console.error("Failed to cleanup terminal sessions:", error); throw error; } + }, + + /** + * Get all model mappings + * @author yovinchen + */ + async getModelMappings(): Promise { + try { + return await invoke("get_model_mappings"); + } catch (error) { + console.error("Failed to get model mappings:", error); + throw error; + } + }, + + /** + * Update a model mapping + * @author yovinchen + */ + async updateModelMapping(alias: string, modelName: string): Promise { + try { + await invoke("update_model_mapping", { alias, modelName }); + } catch (error) { + console.error("Failed to update model mapping:", error); + throw error; + } } }; @@ -2815,3 +2841,13 @@ export const ccrApi = { } } }; + +/** + * Model mapping structure + * @author yovinchen + */ +export interface ModelMapping { + alias: string; + model_name: string; + updated_at: string; +}