mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 00:35:32 +08:00
0c1d94e57b
Extend Rust backend to support provider icon customization:
## Provider Model (src-tauri/src/provider.rs)
- Add `icon: Option<String>` field for icon name
- Add `icon_color: Option<String>` field for hex color
- Use serde rename `iconColor` for frontend compatibility
- Apply skip_serializing_if for clean JSON output
- Update Provider::new() to initialize icon fields as None
## Provider Defaults (src-tauri/src/provider_defaults.rs) [NEW]
- Define ProviderIcon struct with name and color fields
- Create DEFAULT_PROVIDER_ICONS static HashMap with 23 providers:
- AI providers: OpenAI, Anthropic, Claude, Google, Gemini,
DeepSeek, Kimi, Moonshot, Zhipu, MiniMax, Baidu, Alibaba,
Tencent, Meta, Microsoft, Cohere, Perplexity, Mistral, HuggingFace
- Cloud platforms: AWS, Azure, Huawei, Cloudflare
- Implement infer_provider_icon() with exact and fuzzy matching
- Add unit tests for matching logic (exact, fuzzy, case-insensitive)
## Deep Link Support (src-tauri/src/deeplink.rs)
- Initialize icon fields when creating Provider from deep link import
## Module Registration (src-tauri/src/lib.rs)
- Register provider_defaults module
## Dependencies (Cargo.toml)
- Add once_cell for lazy static initialization
This backend support enables icon persistence and future features
like auto-icon inference during provider creation.
239 lines
4.8 KiB
Rust
239 lines
4.8 KiB
Rust
use once_cell::sync::Lazy;
|
|
use std::collections::HashMap;
|
|
|
|
/// 供应商图标信息
|
|
#[derive(Debug, Clone)]
|
|
#[allow(dead_code)]
|
|
pub struct ProviderIcon {
|
|
pub name: &'static str,
|
|
pub color: &'static str,
|
|
}
|
|
|
|
/// 供应商名称到图标的默认映射
|
|
#[allow(dead_code)]
|
|
pub static DEFAULT_PROVIDER_ICONS: Lazy<HashMap<&'static str, ProviderIcon>> = Lazy::new(|| {
|
|
let mut m = HashMap::new();
|
|
|
|
// AI 服务商
|
|
m.insert(
|
|
"openai",
|
|
ProviderIcon {
|
|
name: "openai",
|
|
color: "#00A67E",
|
|
},
|
|
);
|
|
m.insert(
|
|
"anthropic",
|
|
ProviderIcon {
|
|
name: "anthropic",
|
|
color: "#D4915D",
|
|
},
|
|
);
|
|
m.insert(
|
|
"claude",
|
|
ProviderIcon {
|
|
name: "claude",
|
|
color: "#D4915D",
|
|
},
|
|
);
|
|
m.insert(
|
|
"google",
|
|
ProviderIcon {
|
|
name: "google",
|
|
color: "#4285F4",
|
|
},
|
|
);
|
|
m.insert(
|
|
"gemini",
|
|
ProviderIcon {
|
|
name: "gemini",
|
|
color: "#4285F4",
|
|
},
|
|
);
|
|
m.insert(
|
|
"deepseek",
|
|
ProviderIcon {
|
|
name: "deepseek",
|
|
color: "#1E88E5",
|
|
},
|
|
);
|
|
m.insert(
|
|
"kimi",
|
|
ProviderIcon {
|
|
name: "kimi",
|
|
color: "#6366F1",
|
|
},
|
|
);
|
|
m.insert(
|
|
"moonshot",
|
|
ProviderIcon {
|
|
name: "moonshot",
|
|
color: "#6366F1",
|
|
},
|
|
);
|
|
m.insert(
|
|
"zhipu",
|
|
ProviderIcon {
|
|
name: "zhipu",
|
|
color: "#0F62FE",
|
|
},
|
|
);
|
|
m.insert(
|
|
"minimax",
|
|
ProviderIcon {
|
|
name: "minimax",
|
|
color: "#FF6B6B",
|
|
},
|
|
);
|
|
m.insert(
|
|
"baidu",
|
|
ProviderIcon {
|
|
name: "baidu",
|
|
color: "#2932E1",
|
|
},
|
|
);
|
|
m.insert(
|
|
"alibaba",
|
|
ProviderIcon {
|
|
name: "alibaba",
|
|
color: "#FF6A00",
|
|
},
|
|
);
|
|
m.insert(
|
|
"tencent",
|
|
ProviderIcon {
|
|
name: "tencent",
|
|
color: "#00A4FF",
|
|
},
|
|
);
|
|
m.insert(
|
|
"meta",
|
|
ProviderIcon {
|
|
name: "meta",
|
|
color: "#0081FB",
|
|
},
|
|
);
|
|
m.insert(
|
|
"microsoft",
|
|
ProviderIcon {
|
|
name: "microsoft",
|
|
color: "#00A4EF",
|
|
},
|
|
);
|
|
m.insert(
|
|
"cohere",
|
|
ProviderIcon {
|
|
name: "cohere",
|
|
color: "#39594D",
|
|
},
|
|
);
|
|
m.insert(
|
|
"perplexity",
|
|
ProviderIcon {
|
|
name: "perplexity",
|
|
color: "#20808D",
|
|
},
|
|
);
|
|
m.insert(
|
|
"mistral",
|
|
ProviderIcon {
|
|
name: "mistral",
|
|
color: "#FF7000",
|
|
},
|
|
);
|
|
m.insert(
|
|
"huggingface",
|
|
ProviderIcon {
|
|
name: "huggingface",
|
|
color: "#FFD21E",
|
|
},
|
|
);
|
|
|
|
// 云平台
|
|
m.insert(
|
|
"aws",
|
|
ProviderIcon {
|
|
name: "aws",
|
|
color: "#FF9900",
|
|
},
|
|
);
|
|
m.insert(
|
|
"azure",
|
|
ProviderIcon {
|
|
name: "azure",
|
|
color: "#0078D4",
|
|
},
|
|
);
|
|
m.insert(
|
|
"huawei",
|
|
ProviderIcon {
|
|
name: "huawei",
|
|
color: "#FF0000",
|
|
},
|
|
);
|
|
m.insert(
|
|
"cloudflare",
|
|
ProviderIcon {
|
|
name: "cloudflare",
|
|
color: "#F38020",
|
|
},
|
|
);
|
|
|
|
m
|
|
});
|
|
|
|
/// 根据供应商名称智能推断图标
|
|
#[allow(dead_code)]
|
|
pub fn infer_provider_icon(provider_name: &str) -> Option<ProviderIcon> {
|
|
let name_lower = provider_name.to_lowercase();
|
|
|
|
// 精确匹配
|
|
if let Some(icon) = DEFAULT_PROVIDER_ICONS.get(name_lower.as_str()) {
|
|
return Some(icon.clone());
|
|
}
|
|
|
|
// 模糊匹配(包含关键词)
|
|
for (key, icon) in DEFAULT_PROVIDER_ICONS.iter() {
|
|
if name_lower.contains(key) {
|
|
return Some(icon.clone());
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_exact_match() {
|
|
let icon = infer_provider_icon("openai");
|
|
assert!(icon.is_some());
|
|
let icon = icon.unwrap();
|
|
assert_eq!(icon.name, "openai");
|
|
assert_eq!(icon.color, "#00A67E");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fuzzy_match() {
|
|
let icon = infer_provider_icon("OpenAI Official");
|
|
assert!(icon.is_some());
|
|
let icon = icon.unwrap();
|
|
assert_eq!(icon.name, "openai");
|
|
}
|
|
|
|
#[test]
|
|
fn test_case_insensitive() {
|
|
let icon = infer_provider_icon("ANTHROPIC");
|
|
assert!(icon.is_some());
|
|
assert_eq!(icon.unwrap().name, "anthropic");
|
|
}
|
|
|
|
#[test]
|
|
fn test_no_match() {
|
|
let icon = infer_provider_icon("unknown provider");
|
|
assert!(icon.is_none());
|
|
}
|
|
}
|