chore: format code and clean up unused props

- Run cargo fmt on Rust backend code
- Format TypeScript imports and code style
- Remove unused appId prop from ProviderPresetSelector
- Clean up unused variables in tests
- Integrate notes field handling in provider dialogs
This commit is contained in:
YoVinchen
2025-11-17 16:45:57 +08:00
parent ec1ae7073f
commit 6b5752db24
15 changed files with 71 additions and 56 deletions

View File

@@ -317,7 +317,9 @@ impl MultiAppConfig {
// 迁移通用配置片段claude_common_config_snippet → common_config_snippets.claude
if let Some(old_claude_snippet) = config.claude_common_config_snippet.take() {
log::info!("迁移通用配置claude_common_config_snippet → common_config_snippets.claude");
log::info!(
"迁移通用配置claude_common_config_snippet → common_config_snippets.claude"
);
config.common_config_snippets.claude = Some(old_claude_snippet);
updated = true;
}
@@ -414,9 +416,7 @@ impl MultiAppConfig {
return Ok(false);
}
log::info!(
"检测到已存在配置文件且 Prompt 列表为空,将尝试从现有提示词文件自动导入"
);
log::info!("检测到已存在配置文件且 Prompt 列表为空,将尝试从现有提示词文件自动导入");
let mut imported = false;
for app in [AppType::Claude, AppType::Codex, AppType::Gemini] {

View File

@@ -139,13 +139,11 @@ pub fn upsert_mcp_server(id: &str, spec: Value) -> Result<bool, AppError> {
if is_http || is_sse {
let url = spec.get("url").and_then(|x| x.as_str()).unwrap_or("");
if url.is_empty() {
return Err(AppError::McpValidation(
if is_http {
"http 类型的 MCP 服务器缺少 url 字段".into()
} else {
"sse 类型的 MCP 服务器缺少 url 字段".into()
},
));
return Err(AppError::McpValidation(if is_http {
"http 类型的 MCP 服务器缺少 url 字段".into()
} else {
"sse 类型的 MCP 服务器缺少 url 字段".into()
}));
}
}

View File

@@ -184,13 +184,12 @@ pub async fn get_common_config_snippet(
use crate::app_config::AppType;
use std::str::FromStr;
let app = AppType::from_str(&app_type)
.map_err(|e| format!("无效的应用类型: {}", e))?;
let app = AppType::from_str(&app_type).map_err(|e| format!("无效的应用类型: {e}"))?;
let guard = state
.config
.read()
.map_err(|e| format!("读取配置锁失败: {}", e))?;
.map_err(|e| format!("读取配置锁失败: {e}"))?;
Ok(guard.common_config_snippets.get(&app).cloned())
}
@@ -205,13 +204,12 @@ pub async fn set_common_config_snippet(
use crate::app_config::AppType;
use std::str::FromStr;
let app = AppType::from_str(&app_type)
.map_err(|e| format!("无效的应用类型: {}", e))?;
let app = AppType::from_str(&app_type).map_err(|e| format!("无效的应用类型: {e}"))?;
let mut guard = state
.config
.write()
.map_err(|e| format!("写入配置锁失败: {}", e))?;
.map_err(|e| format!("写入配置锁失败: {e}"))?;
// 验证格式(根据应用类型)
if !snippet.trim().is_empty() {
@@ -219,7 +217,7 @@ pub async fn set_common_config_snippet(
AppType::Claude | AppType::Gemini => {
// 验证 JSON 格式
serde_json::from_str::<serde_json::Value>(&snippet)
.map_err(|e| format!("无效的 JSON 格式: {}", e))?;
.map_err(|e| format!("无效的 JSON 格式: {e}"))?;
}
AppType::Codex => {
// TOML 格式暂不验证(或可使用 toml crate

View File

@@ -48,8 +48,6 @@ pub fn read_mcp_json() -> Result<Option<String>, AppError> {
Ok(Some(content))
}
/// 读取 Gemini settings.json 中的 mcpServers 映射
pub fn read_mcp_servers_map() -> Result<std::collections::HashMap<String, Value>, AppError> {
let path = user_config_path();

View File

@@ -396,11 +396,7 @@ pub fn import_from_claude(config: &mut MultiAppConfig) -> Result<usize, AppError
}
if !errors.is_empty() {
log::warn!(
"导入完成,但有 {} 项失败: {:?}",
errors.len(),
errors
);
log::warn!("导入完成,但有 {} 项失败: {:?}", errors.len(), errors);
}
Ok(changed)
@@ -783,11 +779,7 @@ pub fn import_from_gemini(config: &mut MultiAppConfig) -> Result<usize, AppError
}
if !errors.is_empty() {
log::warn!(
"导入完成,但有 {} 项失败: {:?}",
errors.len(),
errors
);
log::warn!("导入完成,但有 {} 项失败: {:?}", errors.len(), errors);
}
Ok(changed)

View File

@@ -488,10 +488,17 @@ url = "https://example.com"
assert!(changed >= 2, "should import both servers");
// v3.7.0: 检查统一结构
let servers = config.mcp.servers.as_ref().expect("unified servers should exist");
let servers = config
.mcp
.servers
.as_ref()
.expect("unified servers should exist");
let echo = servers.get("echo_server").expect("echo server");
assert_eq!(echo.apps.codex, true, "Codex app should be enabled for echo_server");
assert_eq!(
echo.apps.codex, true,
"Codex app should be enabled for echo_server"
);
let server_spec = echo.server.as_object().expect("server spec");
assert_eq!(
server_spec
@@ -502,7 +509,10 @@ url = "https://example.com"
);
let http = servers.get("http_server").expect("http server");
assert_eq!(http.apps.codex, true, "Codex app should be enabled for http_server");
assert_eq!(
http.apps.codex, true,
"Codex app should be enabled for http_server"
);
let http_spec = http.server.as_object().expect("http spec");
assert_eq!(
http_spec.get("url").and_then(|v| v.as_str()).unwrap_or(""),
@@ -541,7 +551,7 @@ command = "echo"
}),
apps: cc_switch_lib::McpApps {
claude: false,
codex: false, // 初始未启用
codex: false, // 初始未启用
gemini: false,
},
description: None,
@@ -564,7 +574,10 @@ command = "echo"
.expect("existing entry");
// 验证 Codex 应用已启用
assert_eq!(entry.apps.codex, true, "Codex app should be enabled after import");
assert_eq!(
entry.apps.codex, true,
"Codex app should be enabled after import"
);
// 验证现有配置被保留server 不应被覆盖)
let spec = entry.server.as_object().expect("server spec");
@@ -662,7 +675,7 @@ fn import_from_claude_merges_into_config() {
"command": "prev"
}),
apps: cc_switch_lib::McpApps {
claude: false, // 初始未启用
claude: false, // 初始未启用
codex: false,
gemini: false,
},
@@ -686,7 +699,10 @@ fn import_from_claude_merges_into_config() {
.expect("entry exists");
// 验证 Claude 应用已启用
assert_eq!(entry.apps.claude, true, "Claude app should be enabled after import");
assert_eq!(
entry.apps.claude, true,
"Claude app should be enabled after import"
);
// 验证现有配置被保留server 不应被覆盖)
let server = entry.server.as_object().expect("server obj");

View File

@@ -127,8 +127,14 @@ fn import_mcp_from_claude_creates_config_and_enables_servers() {
let guard = state.config.read().expect("lock config");
// v3.7.0: 检查统一结构
let servers = guard.mcp.servers.as_ref().expect("unified servers should exist");
let entry = servers.get("echo").expect("server imported into unified structure");
let servers = guard
.mcp
.servers
.as_ref()
.expect("unified servers should exist");
let entry = servers
.get("echo")
.expect("server imported into unified structure");
assert!(
entry.apps.claude,
"imported server should have Claude app enabled"
@@ -182,10 +188,12 @@ fn set_mcp_enabled_for_codex_writes_live_config() {
// 创建 Codex 配置目录和文件
let codex_dir = home.join(".codex");
fs::create_dir_all(&codex_dir).expect("create codex dir");
fs::write(codex_dir.join("auth.json"), r#"{"OPENAI_API_KEY":"test-key"}"#)
.expect("create auth.json");
fs::write(codex_dir.join("config.toml"), "")
.expect("create empty config.toml");
fs::write(
codex_dir.join("auth.json"),
r#"{"OPENAI_API_KEY":"test-key"}"#,
)
.expect("create auth.json");
fs::write(codex_dir.join("config.toml"), "").expect("create empty config.toml");
let mut config = MultiAppConfig::default();
config.ensure_app(&AppType::Codex);
@@ -203,7 +211,7 @@ fn set_mcp_enabled_for_codex_writes_live_config() {
}),
apps: McpApps {
claude: false,
codex: false, // 初始未启用
codex: false, // 初始未启用
gemini: false,
},
description: None,

View File

@@ -1,7 +1,14 @@
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import { Save, Plus, AlertCircle, ChevronDown, ChevronUp, Wand2 } from "lucide-react";
import {
Save,
Plus,
AlertCircle,
ChevronDown,
ChevronUp,
Wand2,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {

View File

@@ -80,7 +80,9 @@ const McpWizardModal: React.FC<McpWizardModalProps> = ({
initialServer,
}) => {
const { t } = useTranslation();
const [wizardType, setWizardType] = useState<"stdio" | "http" | "sse">("stdio");
const [wizardType, setWizardType] = useState<"stdio" | "http" | "sse">(
"stdio",
);
const [wizardTitle, setWizardTitle] = useState("");
// stdio 字段
const [wizardCommand, setWizardCommand] = useState("");

View File

@@ -76,10 +76,7 @@ export function useMcpValidation() {
if (typ === "stdio" && !(obj as any)?.command?.trim()) {
return t("mcp.error.commandRequired");
}
if (
(typ === "http" || typ === "sse") &&
!(obj as any)?.url?.trim()
) {
if ((typ === "http" || typ === "sse") && !(obj as any)?.url?.trim()) {
return t("mcp.wizard.urlRequired");
}
}

View File

@@ -45,6 +45,7 @@ export function AddProviderDialog({
// 构造基础提交数据
const providerData: Omit<Provider, "id"> = {
name: values.name.trim(),
notes: values.notes?.trim() || undefined,
websiteUrl: values.websiteUrl?.trim() || undefined,
settingsConfig: parsedConfig,
...(values.presetCategory ? { category: values.presetCategory } : {}),

View File

@@ -93,6 +93,7 @@ export function EditProviderDialog({
const updatedProvider: Provider = {
...provider,
name: values.name.trim(),
notes: values.notes?.trim() || undefined,
websiteUrl: values.websiteUrl?.trim() || undefined,
settingsConfig: parsedConfig,
...(values.presetCategory ? { category: values.presetCategory } : {}),
@@ -129,6 +130,7 @@ export function EditProviderDialog({
onCancel={() => onOpenChange(false)}
initialData={{
name: provider.name,
notes: provider.notes,
websiteUrl: provider.websiteUrl,
// 若读取到实时配置则优先使用
settingsConfig: initialSettingsConfig,

View File

@@ -623,7 +623,6 @@ export function ProviderForm({
presetCategoryLabels={presetCategoryLabels}
onPresetChange={handlePresetChange}
category={category}
appId={appId}
/>
)}

View File

@@ -6,7 +6,6 @@ import type { ProviderPreset } from "@/config/claudeProviderPresets";
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
import type { GeminiProviderPreset } from "@/config/geminiProviderPresets";
import type { ProviderCategory } from "@/types";
import type { AppId } from "@/lib/api";
type PresetEntry = {
id: string;
@@ -20,7 +19,6 @@ interface ProviderPresetSelectorProps {
presetCategoryLabels: Record<string, string>;
onPresetChange: (value: string) => void;
category?: ProviderCategory; // 当前选中的分类
appId?: AppId;
}
export function ProviderPresetSelector({
@@ -30,7 +28,6 @@ export function ProviderPresetSelector({
presetCategoryLabels,
onPresetChange,
category,
appId,
}: ProviderPresetSelectorProps) {
const { t } = useTranslation();

View File

@@ -220,7 +220,7 @@ describe("McpFormModal", () => {
});
it("缺少配置命令时阻止提交并提示错误", async () => {
const { onSave } = renderForm();
renderForm();
fireEvent.change(screen.getByPlaceholderText("mcp.form.titlePlaceholder"), {
target: { value: "no-command" },
@@ -288,7 +288,7 @@ command = "run"
});
it("TOML 模式下缺少命令时展示错误提示并阻止提交", async () => {
const { onSave } = renderForm({ defaultFormat: "toml" });
renderForm({ defaultFormat: "toml" });
// 填写 ID 字段
fireEvent.change(screen.getByPlaceholderText("mcp.form.titlePlaceholder"), {