feat: 优化UI体验和添加帮助说明 v0.7.2

- 移除冗余的 OpenAI/Claude 自定义 Tab 和可用模型 Tab
- 合并启动/停止服务按钮为单个切换按钮
- 添加可折叠的 HelpTip 组件
- 为凭证池、配置切换、API Server、MCP、Prompts、Skills 页面添加帮助说明
- 在关于页面添加使用说明 Q&A
- 优化日志页面,添加回到顶部按钮
- 优化 Provider 切换提示,显示在框内
- 重命名 AI Clients 为配置切换,说明更清晰
This commit is contained in:
coso
2025-12-15 15:23:10 +08:00
parent b819295379
commit ad00ca48e3
18 changed files with 510 additions and 244 deletions
+1 -1
View File
@@ -3153,7 +3153,7 @@ dependencies = [
[[package]]
name = "proxycast"
version = "0.7.0"
version = "0.7.2"
dependencies = [
"anyhow",
"async-stream",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "proxycast"
version = "0.7.0"
version = "0.7.2"
description = "AI API Proxy Desktop App"
authors = ["you"]
edition = "2021"
+7
View File
@@ -211,6 +211,13 @@ async fn set_default_provider(
let mut s = state.write().await;
s.config.default_provider = provider.clone();
// 同时更新运行中服务器的 default_provider_ref
{
let mut dp = s.default_provider_ref.write().await;
*dp = provider.clone();
}
config::save_config(&s.config).map_err(|e| e.to_string())?;
logs.write()
.await
+83
View File
@@ -59,6 +59,7 @@ pub struct ServerState {
pub qwen_provider: QwenProvider,
pub openai_custom_provider: OpenAICustomProvider,
pub claude_custom_provider: ClaudeCustomProvider,
pub default_provider_ref: Arc<RwLock<String>>,
shutdown_tx: Option<oneshot::Sender<()>>,
}
@@ -69,6 +70,7 @@ impl ServerState {
let qwen = QwenProvider::new();
let openai_custom = OpenAICustomProvider::new();
let claude_custom = ClaudeCustomProvider::new();
let default_provider_ref = Arc::new(RwLock::new(config.default_provider.clone()));
Self {
config,
@@ -80,6 +82,7 @@ impl ServerState {
qwen_provider: qwen,
openai_custom_provider: openai_custom,
claude_custom_provider: claude_custom,
default_provider_ref,
shutdown_tx: None,
}
}
@@ -111,6 +114,7 @@ impl ServerState {
let host = self.config.server.host.clone();
let port = self.config.server.port;
let api_key = self.config.server.api_key.clone();
let default_provider_ref = self.default_provider_ref.clone();
// 重新加载凭证
let _ = self.kiro_provider.load_credentials().await;
@@ -121,6 +125,7 @@ impl ServerState {
&host,
port,
&api_key,
default_provider_ref,
kiro,
logs,
rx,
@@ -163,6 +168,7 @@ impl Clone for KiroProvider {
struct AppState {
api_key: String,
base_url: String,
default_provider: Arc<RwLock<String>>,
kiro: Arc<RwLock<KiroProvider>>,
logs: Arc<RwLock<LogStore>>,
kiro_refresh_lock: Arc<tokio::sync::Mutex<()>>,
@@ -177,6 +183,7 @@ async fn run_server(
host: &str,
port: u16,
api_key: &str,
default_provider: Arc<RwLock<String>>,
kiro: KiroProvider,
logs: Arc<RwLock<LogStore>>,
shutdown: oneshot::Receiver<()>,
@@ -188,6 +195,7 @@ async fn run_server(
let state = AppState {
api_key: api_key.to_string(),
base_url,
default_provider,
kiro: Arc::new(RwLock::new(kiro)),
logs,
kiro_refresh_lock: Arc::new(tokio::sync::Mutex::new(())),
@@ -311,6 +319,42 @@ async fn chat_completions(
),
);
// 获取当前默认 provider
let default_provider = state.default_provider.read().await.clone();
// 尝试从凭证池中选择凭证
let credential = match &state.db {
Some(db) => state
.pool_service
.select_credential(db, &default_provider, Some(&request.model))
.ok()
.flatten(),
None => None,
};
// 如果找到凭证池中的凭证,使用它
if let Some(cred) = credential {
state.logs.write().await.add(
"info",
&format!(
"[ROUTE] Using pool credential: type={} name={:?} uuid={}",
cred.provider_type,
cred.name,
&cred.uuid[..8]
),
);
return call_provider_openai(&state, &cred, &request).await;
}
// 回退到旧的单凭证模式
state.logs.write().await.add(
"debug",
&format!(
"[ROUTE] No pool credential found for '{}', using legacy mode",
default_provider
),
);
// 检查是否需要刷新 token(无 token 或即将过期)
{
let _guard = state.kiro_refresh_lock.lock().await;
@@ -595,6 +639,45 @@ async fn anthropic_messages(
);
}
// 获取当前默认 provider
let default_provider = state.default_provider.read().await.clone();
// 尝试从凭证池中选择凭证
let credential = match &state.db {
Some(db) => {
// 根据 default_provider 配置选择凭证
state
.pool_service
.select_credential(db, &default_provider, Some(&request.model))
.ok()
.flatten()
}
None => None,
};
// 如果找到凭证池中的凭证,使用它
if let Some(cred) = credential {
state.logs.write().await.add(
"info",
&format!(
"[ROUTE] Using pool credential: type={} name={:?} uuid={}",
cred.provider_type,
cred.name,
&cred.uuid[..8]
),
);
return call_provider_anthropic(&state, &cred, &request).await;
}
// 回退到旧的单凭证模式
state.logs.write().await.add(
"debug",
&format!(
"[ROUTE] No pool credential found for '{}', using legacy mode",
default_provider
),
);
// 检查是否需要刷新 token(无 token 或即将过期)
{
let _guard = state.kiro_refresh_lock.lock().await;
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "ProxyCast",
"version": "0.7.0",
"version": "0.7.2",
"identifier": "com.proxycast.app",
"build": {
"beforeDevCommand": "npm run dev",