mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-29 01:05:59 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c932030a24 | |||
| 01abd11e11 | |||
| 67e074c0a7 | |||
| b1c7fe5563 |
@@ -0,0 +1,14 @@
|
|||||||
|
#[tauri::command]
|
||||||
|
pub fn enter_lightweight_mode(app: tauri::AppHandle) -> Result<(), String> {
|
||||||
|
crate::lightweight::enter_lightweight_mode(&app)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn exit_lightweight_mode(app: tauri::AppHandle) -> Result<(), String> {
|
||||||
|
crate::lightweight::exit_lightweight_mode(&app)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn is_lightweight_mode() -> bool {
|
||||||
|
crate::lightweight::is_lightweight_mode()
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ mod sync_support;
|
|||||||
mod usage;
|
mod usage;
|
||||||
mod webdav_sync;
|
mod webdav_sync;
|
||||||
mod workspace;
|
mod workspace;
|
||||||
|
mod lightweight;
|
||||||
|
|
||||||
pub use auth::*;
|
pub use auth::*;
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
@@ -50,3 +51,4 @@ pub use stream_check::*;
|
|||||||
pub use usage::*;
|
pub use usage::*;
|
||||||
pub use webdav_sync::*;
|
pub use webdav_sync::*;
|
||||||
pub use workspace::*;
|
pub use workspace::*;
|
||||||
|
pub use lightweight::*;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ mod error;
|
|||||||
mod gemini_config;
|
mod gemini_config;
|
||||||
mod gemini_mcp;
|
mod gemini_mcp;
|
||||||
mod init_status;
|
mod init_status;
|
||||||
|
mod lightweight;
|
||||||
mod mcp;
|
mod mcp;
|
||||||
mod openclaw_config;
|
mod openclaw_config;
|
||||||
mod opencode_config;
|
mod opencode_config;
|
||||||
@@ -204,6 +205,12 @@ pub fn run() {
|
|||||||
log::debug!(" arg[{i}]: {}", redact_url_for_log(arg));
|
log::debug!(" arg[{i}]: {}", redact_url_for_log(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if crate::lightweight::is_lightweight_mode() {
|
||||||
|
if let Err(e) = crate::lightweight::exit_lightweight_mode(app) {
|
||||||
|
log::error!("退出轻量模式重建窗口失败: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for deep link URL in args (mainly for Windows/Linux command line)
|
// Check for deep link URL in args (mainly for Windows/Linux command line)
|
||||||
let mut found_deeplink = false;
|
let mut found_deeplink = false;
|
||||||
for arg in &args {
|
for arg in &args {
|
||||||
@@ -615,6 +622,12 @@ pub fn run() {
|
|||||||
let urls = event.urls();
|
let urls = event.urls();
|
||||||
log::info!("Received {} URL(s)", urls.len());
|
log::info!("Received {} URL(s)", urls.len());
|
||||||
|
|
||||||
|
if crate::lightweight::is_lightweight_mode() {
|
||||||
|
if let Err(e) = crate::lightweight::exit_lightweight_mode(&app_handle) {
|
||||||
|
log::error!("退出轻量模式重建窗口失败: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (i, url) in urls.iter().enumerate() {
|
for (i, url) in urls.iter().enumerate() {
|
||||||
let url_str = url.as_str();
|
let url_str = url.as_str();
|
||||||
log::debug!(" URL[{i}]: {}", redact_url_for_log(url_str));
|
log::debug!(" URL[{i}]: {}", redact_url_for_log(url_str));
|
||||||
@@ -1085,6 +1098,10 @@ pub fn run() {
|
|||||||
commands::delete_daily_memory_file,
|
commands::delete_daily_memory_file,
|
||||||
commands::search_daily_memory_files,
|
commands::search_daily_memory_files,
|
||||||
commands::open_workspace_directory,
|
commands::open_workspace_directory,
|
||||||
|
// lightweight mode (for testing or low-resource environments)
|
||||||
|
commands::enter_lightweight_mode,
|
||||||
|
commands::exit_lightweight_mode,
|
||||||
|
commands::is_lightweight_mode,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let app = builder
|
let app = builder
|
||||||
@@ -1135,6 +1152,10 @@ pub fn run() {
|
|||||||
let _ = window.show();
|
let _ = window.show();
|
||||||
let _ = window.set_focus();
|
let _ = window.set_focus();
|
||||||
tray::apply_tray_policy(app_handle, true);
|
tray::apply_tray_policy(app_handle, true);
|
||||||
|
} else if crate::lightweight::is_lightweight_mode() {
|
||||||
|
if let Err(e) = crate::lightweight::exit_lightweight_mode(app_handle) {
|
||||||
|
log::error!("退出轻量模式重建窗口失败: {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 处理通过自定义 URL 协议触发的打开事件(例如 ccswitch://...)
|
// 处理通过自定义 URL 协议触发的打开事件(例如 ccswitch://...)
|
||||||
@@ -1144,6 +1165,13 @@ pub fn run() {
|
|||||||
log::info!("RunEvent::Opened with URL: {url_str}");
|
log::info!("RunEvent::Opened with URL: {url_str}");
|
||||||
|
|
||||||
if url_str.starts_with("ccswitch://") {
|
if url_str.starts_with("ccswitch://") {
|
||||||
|
if crate::lightweight::is_lightweight_mode() {
|
||||||
|
if let Err(e) = crate::lightweight::exit_lightweight_mode(app_handle)
|
||||||
|
{
|
||||||
|
log::error!("退出轻量模式重建窗口失败: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 解析并广播深链接事件,复用与 single_instance 相同的逻辑
|
// 解析并广播深链接事件,复用与 single_instance 相同的逻辑
|
||||||
match crate::deeplink::parse_deeplink_url(&url_str) {
|
match crate::deeplink::parse_deeplink_url(&url_str) {
|
||||||
Ok(request) => {
|
Ok(request) => {
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
use tauri::Manager;
|
||||||
|
|
||||||
|
static LIGHTWEIGHT_MODE: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
pub fn enter_lightweight_mode(app: &tauri::AppHandle) -> Result<(), String> {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
|
let _ = window.set_skip_taskbar(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
crate::tray::apply_tray_policy(app, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
|
window
|
||||||
|
.destroy()
|
||||||
|
.map_err(|e| format!("销毁主窗口失败: {e}"))?;
|
||||||
|
}
|
||||||
|
// else: already in lightweight mode or window not found, just set the flag
|
||||||
|
|
||||||
|
LIGHTWEIGHT_MODE.store(true, Ordering::Release);
|
||||||
|
crate::tray::refresh_tray_menu(app);
|
||||||
|
log::info!("进入轻量模式");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_lightweight_mode(app: &tauri::AppHandle) -> Result<(), String> {
|
||||||
|
use tauri::WebviewWindowBuilder;
|
||||||
|
|
||||||
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
|
let _ = window.unminimize();
|
||||||
|
let _ = window.show();
|
||||||
|
let _ = window.set_focus();
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
let _ = window.set_skip_taskbar(false);
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
crate::tray::apply_tray_policy(app, true);
|
||||||
|
}
|
||||||
|
LIGHTWEIGHT_MODE.store(false, Ordering::Release);
|
||||||
|
crate::tray::refresh_tray_menu(app);
|
||||||
|
log::info!("退出轻量模式");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let window_config = app
|
||||||
|
.config()
|
||||||
|
.app
|
||||||
|
.windows
|
||||||
|
.iter()
|
||||||
|
.find(|w| w.label == "main")
|
||||||
|
.ok_or("主窗口配置未找到")?;
|
||||||
|
|
||||||
|
WebviewWindowBuilder::from_config(app, window_config)
|
||||||
|
.map_err(|e| format!("加载主窗口配置失败: {e}"))?
|
||||||
|
.visible(true)
|
||||||
|
.build()
|
||||||
|
.map_err(|e| format!("创建主窗口失败: {e}"))?;
|
||||||
|
|
||||||
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
|
let _ = window.set_focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
|
let _ = window.set_skip_taskbar(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
crate::tray::apply_tray_policy(app, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIGHTWEIGHT_MODE.store(false, Ordering::Release);
|
||||||
|
crate::tray::refresh_tray_menu(app);
|
||||||
|
log::info!("退出轻量模式");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_lightweight_mode() -> bool {
|
||||||
|
LIGHTWEIGHT_MODE.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ use crate::store::AppState;
|
|||||||
pub struct TrayTexts {
|
pub struct TrayTexts {
|
||||||
pub show_main: &'static str,
|
pub show_main: &'static str,
|
||||||
pub no_provider_hint: &'static str,
|
pub no_provider_hint: &'static str,
|
||||||
|
pub lightweight_mode: &'static str,
|
||||||
pub quit: &'static str,
|
pub quit: &'static str,
|
||||||
pub _auto_label: &'static str,
|
pub _auto_label: &'static str,
|
||||||
}
|
}
|
||||||
@@ -24,6 +25,7 @@ impl TrayTexts {
|
|||||||
"en" => Self {
|
"en" => Self {
|
||||||
show_main: "Open main window",
|
show_main: "Open main window",
|
||||||
no_provider_hint: " (No providers yet, please add them from the main window)",
|
no_provider_hint: " (No providers yet, please add them from the main window)",
|
||||||
|
lightweight_mode: "Lightweight Mode",
|
||||||
quit: "Quit",
|
quit: "Quit",
|
||||||
_auto_label: "Auto (Failover)",
|
_auto_label: "Auto (Failover)",
|
||||||
},
|
},
|
||||||
@@ -31,12 +33,14 @@ impl TrayTexts {
|
|||||||
show_main: "メインウィンドウを開く",
|
show_main: "メインウィンドウを開く",
|
||||||
no_provider_hint:
|
no_provider_hint:
|
||||||
" (プロバイダーがまだありません。メイン画面から追加してください)",
|
" (プロバイダーがまだありません。メイン画面から追加してください)",
|
||||||
|
lightweight_mode: "軽量モード",
|
||||||
quit: "終了",
|
quit: "終了",
|
||||||
_auto_label: "自動 (フェイルオーバー)",
|
_auto_label: "自動 (フェイルオーバー)",
|
||||||
},
|
},
|
||||||
_ => Self {
|
_ => Self {
|
||||||
show_main: "打开主界面",
|
show_main: "打开主界面",
|
||||||
no_provider_hint: " (无供应商,请在主界面添加)",
|
no_provider_hint: " (无供应商,请在主界面添加)",
|
||||||
|
lightweight_mode: "轻量模式",
|
||||||
quit: "退出",
|
quit: "退出",
|
||||||
_auto_label: "自动 (故障转移)",
|
_auto_label: "自动 (故障转移)",
|
||||||
},
|
},
|
||||||
@@ -382,6 +386,19 @@ pub fn create_tray_menu(
|
|||||||
menu_builder = menu_builder.separator();
|
menu_builder = menu_builder.separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lightweight_item = CheckMenuItem::with_id(
|
||||||
|
app,
|
||||||
|
"lightweight_mode",
|
||||||
|
tray_texts.lightweight_mode,
|
||||||
|
true,
|
||||||
|
crate::lightweight::is_lightweight_mode(),
|
||||||
|
None::<&str>,
|
||||||
|
).map_err(|e| AppError::Message(format!("创建轻量模式菜单失败: {e}")))?;
|
||||||
|
|
||||||
|
menu_builder = menu_builder
|
||||||
|
.item(&lightweight_item)
|
||||||
|
.separator();
|
||||||
|
|
||||||
// 退出菜单(分隔符已在上面的 section 循环中添加)
|
// 退出菜单(分隔符已在上面的 section 循环中添加)
|
||||||
let quit_item = MenuItem::with_id(app, "quit", tray_texts.quit, true, None::<&str>)
|
let quit_item = MenuItem::with_id(app, "quit", tray_texts.quit, true, None::<&str>)
|
||||||
.map_err(|e| AppError::Message(format!("创建退出菜单失败: {e}")))?;
|
.map_err(|e| AppError::Message(format!("创建退出菜单失败: {e}")))?;
|
||||||
@@ -393,6 +410,20 @@ pub fn create_tray_menu(
|
|||||||
.map_err(|e| AppError::Message(format!("构建菜单失败: {e}")))
|
.map_err(|e| AppError::Message(format!("构建菜单失败: {e}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn refresh_tray_menu(app: &tauri::AppHandle) {
|
||||||
|
use crate::store::AppState;
|
||||||
|
|
||||||
|
if let Some(state) = app.try_state::<AppState>() {
|
||||||
|
if let Ok(new_menu) = create_tray_menu(app, state.inner()) {
|
||||||
|
if let Some(tray) = app.tray_by_id("main") {
|
||||||
|
if let Err(e) = tray.set_menu(Some(new_menu)) {
|
||||||
|
log::error!("刷新托盘菜单失败: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub fn apply_tray_policy(app: &tauri::AppHandle, dock_visible: bool) {
|
pub fn apply_tray_policy(app: &tauri::AppHandle, dock_visible: bool) {
|
||||||
use tauri::ActivationPolicy;
|
use tauri::ActivationPolicy;
|
||||||
@@ -430,6 +461,21 @@ pub fn handle_tray_menu_event(app: &tauri::AppHandle, event_id: &str) {
|
|||||||
{
|
{
|
||||||
apply_tray_policy(app, true);
|
apply_tray_policy(app, true);
|
||||||
}
|
}
|
||||||
|
} else if crate::lightweight::is_lightweight_mode() {
|
||||||
|
if let Err(e) = crate::lightweight::exit_lightweight_mode(app) {
|
||||||
|
log::error!("退出轻量模式重建窗口失败: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"lightweight_mode" => {
|
||||||
|
if crate::lightweight::is_lightweight_mode() {
|
||||||
|
if let Err(e) = crate::lightweight::exit_lightweight_mode(app) {
|
||||||
|
log::error!("退出轻量模式失败: {e}");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Err(e) = crate::lightweight::enter_lightweight_mode(app) {
|
||||||
|
log::error!("进入轻量模式失败: {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"quit" => {
|
"quit" => {
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ export function ProviderCard({
|
|||||||
// OMO and OMO Slim share the same card behavior
|
// OMO and OMO Slim share the same card behavior
|
||||||
const isAnyOmo = isOmo || isOmoSlim;
|
const isAnyOmo = isOmo || isOmoSlim;
|
||||||
const handleDisableAnyOmo = isOmoSlim ? onDisableOmoSlim : onDisableOmo;
|
const handleDisableAnyOmo = isOmoSlim ? onDisableOmoSlim : onDisableOmo;
|
||||||
|
const isAdditiveMode = appId === "opencode" && !isAnyOmo;
|
||||||
|
|
||||||
const { data: health } = useProviderHealth(provider.id, appId);
|
const { data: health } = useProviderHealth(provider.id, appId);
|
||||||
|
|
||||||
@@ -209,9 +210,12 @@ export function ProviderCard({
|
|||||||
: isCurrent;
|
: isCurrent;
|
||||||
|
|
||||||
const shouldUseGreen = !isAnyOmo && isProxyTakeover && isActiveProvider;
|
const shouldUseGreen = !isAnyOmo && isProxyTakeover && isActiveProvider;
|
||||||
|
const hasPersistentConfigHighlight = isAdditiveMode && isInConfig;
|
||||||
const shouldUseBlue =
|
const shouldUseBlue =
|
||||||
(isAnyOmo && isActiveProvider) ||
|
(isAnyOmo && isActiveProvider) ||
|
||||||
(!isAnyOmo && !isProxyTakeover && isActiveProvider);
|
(!isAnyOmo &&
|
||||||
|
!isProxyTakeover &&
|
||||||
|
(isActiveProvider || hasPersistentConfigHighlight));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -224,7 +228,8 @@ export function ProviderCard({
|
|||||||
shouldUseGreen &&
|
shouldUseGreen &&
|
||||||
"border-emerald-500/60 shadow-sm shadow-emerald-500/10",
|
"border-emerald-500/60 shadow-sm shadow-emerald-500/10",
|
||||||
shouldUseBlue && "border-blue-500/60 shadow-sm shadow-blue-500/10",
|
shouldUseBlue && "border-blue-500/60 shadow-sm shadow-blue-500/10",
|
||||||
!isActiveProvider && "hover:shadow-sm",
|
!(isActiveProvider || hasPersistentConfigHighlight) &&
|
||||||
|
"hover:shadow-sm",
|
||||||
dragHandleProps?.isDragging &&
|
dragHandleProps?.isDragging &&
|
||||||
"cursor-grabbing border-primary shadow-lg scale-105 z-10",
|
"cursor-grabbing border-primary shadow-lg scale-105 z-10",
|
||||||
)}
|
)}
|
||||||
@@ -234,8 +239,10 @@ export function ProviderCard({
|
|||||||
"absolute inset-0 bg-gradient-to-r to-transparent transition-opacity duration-500 pointer-events-none",
|
"absolute inset-0 bg-gradient-to-r to-transparent transition-opacity duration-500 pointer-events-none",
|
||||||
shouldUseGreen && "from-emerald-500/10",
|
shouldUseGreen && "from-emerald-500/10",
|
||||||
shouldUseBlue && "from-blue-500/10",
|
shouldUseBlue && "from-blue-500/10",
|
||||||
!isActiveProvider && "from-primary/10",
|
!shouldUseGreen && !shouldUseBlue && "from-primary/10",
|
||||||
isActiveProvider ? "opacity-100" : "opacity-0",
|
isActiveProvider || hasPersistentConfigHighlight
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="relative flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div className="relative flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
|||||||
Reference in New Issue
Block a user