chore: format code and fix bundle identifier for v3.0.0 release

- Format all TypeScript/React code with Prettier
- Format all Rust code with cargo fmt
- Fix bundle identifier from .app to .desktop to avoid macOS conflicts
- Prepare codebase for v3.0.0 Tauri release
This commit is contained in:
Jason
2025-08-27 11:00:53 +08:00
parent 5e2e80b00d
commit 642e7a3817
23 changed files with 359 additions and 321 deletions

View File

@@ -1,3 +1,3 @@
fn main() {
tauri_build::build()
tauri_build::build()
}

View File

@@ -2,46 +2,48 @@ use std::collections::HashMap;
use tauri::State;
use tauri_plugin_opener::OpenerExt;
use crate::config::{
import_current_config_as_default, get_claude_settings_path,
ConfigStatus,
};
use crate::config::{ConfigStatus, get_claude_settings_path, import_current_config_as_default};
use crate::provider::Provider;
use crate::store::AppState;
/// 获取所有供应商
#[tauri::command]
pub async fn get_providers(state: State<'_, AppState>) -> Result<HashMap<String, Provider>, String> {
let manager = state.provider_manager.lock()
pub async fn get_providers(
state: State<'_, AppState>,
) -> Result<HashMap<String, Provider>, String> {
let manager = state
.provider_manager
.lock()
.map_err(|e| format!("获取锁失败: {}", e))?;
Ok(manager.get_all_providers().clone())
}
/// 获取当前供应商ID
#[tauri::command]
pub async fn get_current_provider(state: State<'_, AppState>) -> Result<String, String> {
let manager = state.provider_manager.lock()
let manager = state
.provider_manager
.lock()
.map_err(|e| format!("获取锁失败: {}", e))?;
Ok(manager.current.clone())
}
/// 添加供应商
#[tauri::command]
pub async fn add_provider(
state: State<'_, AppState>,
provider: Provider,
) -> Result<bool, String> {
let mut manager = state.provider_manager.lock()
pub async fn add_provider(state: State<'_, AppState>, provider: Provider) -> Result<bool, String> {
let mut manager = state
.provider_manager
.lock()
.map_err(|e| format!("获取锁失败: {}", e))?;
manager.add_provider(provider)?;
// 保存配置
drop(manager); // 释放锁
state.save()?;
Ok(true)
}
@@ -51,59 +53,57 @@ pub async fn update_provider(
state: State<'_, AppState>,
provider: Provider,
) -> Result<bool, String> {
let mut manager = state.provider_manager.lock()
let mut manager = state
.provider_manager
.lock()
.map_err(|e| format!("获取锁失败: {}", e))?;
manager.update_provider(provider)?;
// 保存配置
drop(manager); // 释放锁
state.save()?;
Ok(true)
}
/// 删除供应商
#[tauri::command]
pub async fn delete_provider(
state: State<'_, AppState>,
id: String,
) -> Result<bool, String> {
let mut manager = state.provider_manager.lock()
pub async fn delete_provider(state: State<'_, AppState>, id: String) -> Result<bool, String> {
let mut manager = state
.provider_manager
.lock()
.map_err(|e| format!("获取锁失败: {}", e))?;
manager.delete_provider(&id)?;
// 保存配置
drop(manager); // 释放锁
state.save()?;
Ok(true)
}
/// 切换供应商
#[tauri::command]
pub async fn switch_provider(
state: State<'_, AppState>,
id: String,
) -> Result<bool, String> {
let mut manager = state.provider_manager.lock()
pub async fn switch_provider(state: State<'_, AppState>, id: String) -> Result<bool, String> {
let mut manager = state
.provider_manager
.lock()
.map_err(|e| format!("获取锁失败: {}", e))?;
manager.switch_provider(&id)?;
// 保存配置
drop(manager); // 释放锁
state.save()?;
Ok(true)
}
/// 导入当前配置为默认供应商
#[tauri::command]
pub async fn import_default_config(
state: State<'_, AppState>,
) -> Result<bool, String> {
pub async fn import_default_config(state: State<'_, AppState>) -> Result<bool, String> {
// 若已存在 default 供应商,则直接返回,避免重复导入
{
let manager = state
@@ -117,7 +117,7 @@ pub async fn import_default_config(
// 导入配置
let settings_config = import_current_config_as_default()?;
// 创建默认供应商
let provider = Provider::with_id(
"default".to_string(),
@@ -125,22 +125,24 @@ pub async fn import_default_config(
settings_config,
None,
);
// 添加到管理器
let mut manager = state.provider_manager.lock()
let mut manager = state
.provider_manager
.lock()
.map_err(|e| format!("获取锁失败: {}", e))?;
manager.add_provider(provider)?;
// 如果没有当前供应商,设置为 default
if manager.current.is_empty() {
manager.current = "default".to_string();
}
// 保存配置
drop(manager); // 释放锁
state.save()?;
Ok(true)
}
@@ -160,18 +162,17 @@ pub async fn get_claude_code_config_path() -> Result<String, String> {
#[tauri::command]
pub async fn open_config_folder(app: tauri::AppHandle) -> Result<bool, String> {
let config_dir = crate::config::get_claude_config_dir();
// 确保目录存在
if !config_dir.exists() {
std::fs::create_dir_all(&config_dir)
.map_err(|e| format!("创建目录失败: {}", e))?;
std::fs::create_dir_all(&config_dir).map_err(|e| format!("创建目录失败: {}", e))?;
}
// 使用 opener 插件打开文件夹
app.opener()
.open_path(config_dir.to_string_lossy().to_string(), None::<String>)
.map_err(|e| format!("打开文件夹失败: {}", e))?;
Ok(true)
}
@@ -184,11 +185,11 @@ pub async fn open_external(app: tauri::AppHandle, url: String) -> Result<bool, S
} else {
format!("https://{}", url)
};
// 使用 opener 插件打开链接
app.opener()
.open_url(&url, None::<String>)
.map_err(|e| format!("打开链接失败: {}", e))?;
Ok(true)
}

View File

@@ -1,7 +1,7 @@
use std::fs;
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fs;
use std::path::{Path, PathBuf};
/// 获取 Claude Code 配置目录路径
pub fn get_claude_config_dir() -> PathBuf {
@@ -49,7 +49,7 @@ pub fn get_provider_config_path(provider_id: &str, provider_name: Option<&str>)
let base_name = provider_name
.map(|name| sanitize_provider_name(name))
.unwrap_or_else(|| sanitize_provider_name(provider_id));
get_claude_config_dir().join(format!("settings-{}.json", base_name))
}
@@ -58,41 +58,35 @@ pub fn read_json_file<T: for<'a> Deserialize<'a>>(path: &Path) -> Result<T, Stri
if !path.exists() {
return Err(format!("文件不存在: {}", path.display()));
}
let content = fs::read_to_string(path)
.map_err(|e| format!("读取文件失败: {}", e))?;
serde_json::from_str(&content)
.map_err(|e| format!("解析 JSON 失败: {}", e))
let content = fs::read_to_string(path).map_err(|e| format!("读取文件失败: {}", e))?;
serde_json::from_str(&content).map_err(|e| format!("解析 JSON 失败: {}", e))
}
/// 写入 JSON 配置文件
pub fn write_json_file<T: Serialize>(path: &Path, data: &T) -> Result<(), String> {
// 确保目录存在
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.map_err(|e| format!("创建目录失败: {}", e))?;
fs::create_dir_all(parent).map_err(|e| format!("创建目录失败: {}", e))?;
}
let json = serde_json::to_string_pretty(data)
.map_err(|e| format!("序列化 JSON 失败: {}", e))?;
fs::write(path, json)
.map_err(|e| format!("写入文件失败: {}", e))
let json =
serde_json::to_string_pretty(data).map_err(|e| format!("序列化 JSON 失败: {}", e))?;
fs::write(path, json).map_err(|e| format!("写入文件失败: {}", e))
}
/// 复制文件
pub fn copy_file(from: &Path, to: &Path) -> Result<(), String> {
fs::copy(from, to)
.map_err(|e| format!("复制文件失败: {}", e))?;
fs::copy(from, to).map_err(|e| format!("复制文件失败: {}", e))?;
Ok(())
}
/// 删除文件
pub fn delete_file(path: &Path) -> Result<(), String> {
if path.exists() {
fs::remove_file(path)
.map_err(|e| format!("删除文件失败: {}", e))?;
fs::remove_file(path).map_err(|e| format!("删除文件失败: {}", e))?;
}
Ok(())
}
@@ -125,18 +119,18 @@ pub fn backup_config(from: &Path, to: &Path) -> Result<(), String> {
/// 导入当前 Claude Code 配置为默认供应商
pub fn import_current_config_as_default() -> Result<Value, String> {
let settings_path = get_claude_settings_path();
if !settings_path.exists() {
return Err("Claude Code 配置文件不存在".to_string());
}
// 读取当前配置
let settings_config: Value = read_json_file(&settings_path)?;
// 保存为 default 供应商
let default_provider_path = get_provider_config_path("default", Some("default"));
write_json_file(&default_provider_path, &settings_config)?;
log::info!("已导入当前配置为默认供应商");
Ok(settings_config)
}

View File

@@ -1,7 +1,7 @@
mod commands;
mod config;
mod provider;
mod store;
mod commands;
use store::AppState;
use tauri::Manager;
@@ -16,33 +16,32 @@ pub fn run() {
{
// 设置 macOS 标题栏背景色为主界面蓝色
if let Some(window) = app.get_webview_window("main") {
use objc2::runtime::AnyObject;
use objc2::rc::Retained;
use objc2::runtime::AnyObject;
use objc2_app_kit::NSColor;
let ns_window_ptr = window.ns_window().unwrap();
let ns_window: Retained<AnyObject> = unsafe {
Retained::retain(ns_window_ptr as *mut AnyObject).unwrap()
};
let ns_window: Retained<AnyObject> =
unsafe { Retained::retain(ns_window_ptr as *mut AnyObject).unwrap() };
// 使用与主界面 banner 相同的蓝色 #3498db
// #3498db = RGB(52, 152, 219)
let bg_color = unsafe {
NSColor::colorWithRed_green_blue_alpha(
52.0/255.0, // R: 52
152.0/255.0, // G: 152
219.0/255.0, // B: 219
1.0, // Alpha: 1.0
52.0 / 255.0, // R: 52
152.0 / 255.0, // G: 152
219.0 / 255.0, // B: 219
1.0, // Alpha: 1.0
)
};
unsafe {
use objc2::msg_send;
let _: () = msg_send![&*ns_window, setBackgroundColor: &*bg_color];
}
}
}
// 初始化日志
if cfg!(debug_assertions) {
app.handle().plugin(
@@ -51,20 +50,20 @@ pub fn run() {
.build(),
)?;
}
// 初始化应用状态(仅创建一次,并在本函数末尾注入 manage
let app_state = AppState::new();
// 如果没有供应商且存在 Claude Code 配置,自动导入
{
let manager = app_state.provider_manager.lock().unwrap();
if manager.providers.is_empty() {
drop(manager); // 释放锁
let settings_path = config::get_claude_settings_path();
if settings_path.exists() {
log::info!("检测到 Claude Code 配置,自动导入为默认供应商");
if let Ok(settings_config) = config::import_current_config_as_default() {
let mut manager = app_state.provider_manager.lock().unwrap();
let provider = provider::Provider::with_id(
@@ -73,7 +72,7 @@ pub fn run() {
settings_config,
None,
);
if manager.add_provider(provider).is_ok() {
manager.current = "default".to_string();
drop(manager);
@@ -84,7 +83,7 @@ pub fn run() {
}
}
}
// 将同一个实例注入到全局状态,避免重复创建导致的不一致
app.manage(app_state);
Ok(())

View File

@@ -2,5 +2,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
cc_switch_lib::run();
cc_switch_lib::run();
}

View File

@@ -4,8 +4,8 @@ use std::collections::HashMap;
use std::path::Path;
use crate::config::{
copy_file, delete_file, get_provider_config_path, read_json_file, write_json_file,
get_claude_settings_path, backup_config
backup_config, copy_file, delete_file, get_claude_settings_path, get_provider_config_path,
read_json_file, write_json_file,
};
/// 供应商结构体
@@ -22,7 +22,12 @@ pub struct Provider {
impl Provider {
/// 从现有ID创建供应商
pub fn with_id(id: String, name: String, settings_config: Value, website_url: Option<String>) -> Self {
pub fn with_id(
id: String,
name: String,
settings_config: Value,
website_url: Option<String>,
) -> Self {
Self {
id,
name,
@@ -55,110 +60,118 @@ impl ProviderManager {
log::info!("配置文件不存在,创建新的供应商管理器");
return Ok(Self::default());
}
read_json_file(path)
}
/// 保存供应商列表
pub fn save_to_file(&self, path: &Path) -> Result<(), String> {
write_json_file(path, self)
}
/// 添加供应商
pub fn add_provider(&mut self, provider: Provider) -> Result<(), String> {
// 保存供应商配置到独立文件
let config_path = get_provider_config_path(&provider.id, Some(&provider.name));
write_json_file(&config_path, &provider.settings_config)?;
// 添加到管理器
self.providers.insert(provider.id.clone(), provider);
Ok(())
}
/// 更新供应商
pub fn update_provider(&mut self, provider: Provider) -> Result<(), String> {
// 检查供应商是否存在
if !self.providers.contains_key(&provider.id) {
return Err(format!("供应商不存在: {}", provider.id));
}
// 如果名称改变了,需要处理配置文件
if let Some(old_provider) = self.providers.get(&provider.id) {
if old_provider.name != provider.name {
// 删除旧配置文件
let old_config_path = get_provider_config_path(&provider.id, Some(&old_provider.name));
let old_config_path =
get_provider_config_path(&provider.id, Some(&old_provider.name));
delete_file(&old_config_path).ok(); // 忽略删除错误
}
}
// 保存新配置文件
let config_path = get_provider_config_path(&provider.id, Some(&provider.name));
write_json_file(&config_path, &provider.settings_config)?;
// 更新管理器
self.providers.insert(provider.id.clone(), provider);
Ok(())
}
/// 删除供应商
pub fn delete_provider(&mut self, provider_id: &str) -> Result<(), String> {
// 检查是否为当前供应商
if self.current == provider_id {
return Err("不能删除当前正在使用的供应商".to_string());
}
// 获取供应商信息
let provider = self.providers.get(provider_id)
let provider = self
.providers
.get(provider_id)
.ok_or_else(|| format!("供应商不存在: {}", provider_id))?;
// 删除配置文件
let config_path = get_provider_config_path(provider_id, Some(&provider.name));
delete_file(&config_path)?;
// 从管理器删除
self.providers.remove(provider_id);
Ok(())
}
/// 切换供应商
pub fn switch_provider(&mut self, provider_id: &str) -> Result<(), String> {
// 检查供应商是否存在
let provider = self.providers.get(provider_id)
let provider = self
.providers
.get(provider_id)
.ok_or_else(|| format!("供应商不存在: {}", provider_id))?;
let settings_path = get_claude_settings_path();
let provider_config_path = get_provider_config_path(provider_id, Some(&provider.name));
// 检查供应商配置文件是否存在
if !provider_config_path.exists() {
return Err(format!("供应商配置文件不存在: {}", provider_config_path.display()));
return Err(format!(
"供应商配置文件不存在: {}",
provider_config_path.display()
));
}
// 如果当前有配置,先备份到当前供应商
if settings_path.exists() && !self.current.is_empty() {
if let Some(current_provider) = self.providers.get(&self.current) {
let current_provider_path = get_provider_config_path(&self.current, Some(&current_provider.name));
let current_provider_path =
get_provider_config_path(&self.current, Some(&current_provider.name));
backup_config(&settings_path, &current_provider_path)?;
log::info!("已备份当前供应商配置: {}", current_provider.name);
}
}
// 确保主配置父目录存在
if let Some(parent) = settings_path.parent() {
std::fs::create_dir_all(parent)
.map_err(|e| format!("创建目录失败: {}", e))?;
std::fs::create_dir_all(parent).map_err(|e| format!("创建目录失败: {}", e))?;
}
// 复制新供应商配置到主配置
copy_file(&provider_config_path, &settings_path)?;
// 更新当前供应商
self.current = provider_id.to_string();
log::info!("成功切换到供应商: {}", provider.name);
Ok(())
}
/// 获取所有供应商
pub fn get_all_providers(&self) -> &HashMap<String, Provider> {
&self.providers

View File

@@ -1,6 +1,6 @@
use std::sync::Mutex;
use crate::config::get_app_config_path;
use crate::provider::ProviderManager;
use std::sync::Mutex;
/// 全局应用状态
pub struct AppState {
@@ -11,25 +11,26 @@ impl AppState {
/// 创建新的应用状态
pub fn new() -> Self {
let config_path = get_app_config_path();
let provider_manager = ProviderManager::load_from_file(&config_path)
.unwrap_or_else(|e| {
log::warn!("加载配置失败: {}, 使用默认配置", e);
ProviderManager::default()
});
let provider_manager = ProviderManager::load_from_file(&config_path).unwrap_or_else(|e| {
log::warn!("加载配置失败: {}, 使用默认配置", e);
ProviderManager::default()
});
Self {
provider_manager: Mutex::new(provider_manager),
}
}
/// 保存配置到文件
pub fn save(&self) -> Result<(), String> {
let config_path = get_app_config_path();
let manager = self.provider_manager.lock()
let manager = self
.provider_manager
.lock()
.map_err(|e| format!("获取锁失败: {}", e))?;
manager.save_to_file(&config_path)
}
// 保留按需扩展:若未来需要热加载,可在此实现
}

View File

@@ -2,7 +2,7 @@
"$schema": "https://schema.tauri.app/config/2",
"productName": "CC Switch",
"version": "3.0.0",
"identifier": "com.ccswitch.app",
"identifier": "com.ccswitch.desktop",
"build": {
"frontendDist": "../dist",
"devUrl": "http://localhost:3000",