use serde::{Deserialize, Serialize}; use crate::proxy::ProxyConfig; use crate::modules::cloudflared::CloudflaredConfig; /// Application configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AppConfig { pub language: String, pub theme: String, pub auto_refresh: bool, pub refresh_interval: i32, // minutes pub auto_sync: bool, pub sync_interval: i32, // minutes pub default_export_path: Option, #[serde(default)] pub proxy: ProxyConfig, pub antigravity_executable: Option, // [NEW] Manually specified Antigravity executable path pub antigravity_args: Option>, // [NEW] Antigravity startup arguments #[serde(default)] pub auto_launch: bool, // Launch on startup #[serde(default)] pub scheduled_warmup: ScheduledWarmupConfig, // [NEW] Scheduled warmup configuration #[serde(default)] pub quota_protection: QuotaProtectionConfig, // [NEW] Quota protection configuration #[serde(default)] pub pinned_quota_models: PinnedQuotaModelsConfig, // [NEW] Pinned quota models list #[serde(default)] pub circuit_breaker: CircuitBreakerConfig, // [NEW] Circuit breaker configuration #[serde(default)] pub hidden_menu_items: Vec, // Hidden menu item path list #[serde(default)] pub cloudflared: CloudflaredConfig, // [NEW] Cloudflared configuration } /// Scheduled warmup configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ScheduledWarmupConfig { /// Whether smart warmup is enabled pub enabled: bool, /// List of models to warmup #[serde(default = "default_warmup_models")] pub monitored_models: Vec, } fn default_warmup_models() -> Vec { vec![ "gemini-3-flash".to_string(), "claude".to_string(), "gemini-3-pro-high".to_string(), "gemini-3-pro-image".to_string(), ] } impl ScheduledWarmupConfig { pub fn new() -> Self { Self { enabled: false, monitored_models: default_warmup_models(), } } } impl Default for ScheduledWarmupConfig { fn default() -> Self { Self::new() } } /// Quota protection configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct QuotaProtectionConfig { /// Whether quota protection is enabled pub enabled: bool, /// Reserved quota percentage (1-99) pub threshold_percentage: u32, /// List of monitored models (e.g. gemini-3-flash, gemini-3-pro-high, gemini-3.1-pro-high, claude-sonnet-4-6) #[serde(default = "default_monitored_models")] pub monitored_models: Vec, } fn default_monitored_models() -> Vec { vec![ "claude".to_string(), "gemini-3-pro-high".to_string(), "gemini-3-flash".to_string(), "gemini-3-pro-image".to_string(), ] } impl QuotaProtectionConfig { pub fn new() -> Self { Self { enabled: false, threshold_percentage: 10, // Default 10% reserve monitored_models: default_monitored_models(), } } } impl Default for QuotaProtectionConfig { fn default() -> Self { Self::new() } } /// Pinned quota models configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PinnedQuotaModelsConfig { /// List of pinned models (displayed outside the account list) #[serde(default = "default_pinned_models")] pub models: Vec, } fn default_pinned_models() -> Vec { vec![ "gemini-3-pro-high".to_string(), "gemini-3-flash".to_string(), "gemini-3-pro-image".to_string(), "claude-sonnet-4-6-thinking".to_string(), ] } impl PinnedQuotaModelsConfig { pub fn new() -> Self { Self { models: default_pinned_models(), } } } impl Default for PinnedQuotaModelsConfig { fn default() -> Self { Self::new() } } /// Circuit breaker configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CircuitBreakerConfig { /// Whether circuit breaker is enabled pub enabled: bool, /// Unified backoff steps (seconds) /// Default: [60, 300, 1800, 7200] #[serde(default = "default_backoff_steps")] pub backoff_steps: Vec, } fn default_backoff_steps() -> Vec { vec![60, 300, 1800, 7200] } impl CircuitBreakerConfig { pub fn new() -> Self { Self { enabled: true, backoff_steps: default_backoff_steps(), } } } impl Default for CircuitBreakerConfig { fn default() -> Self { Self::new() } } impl AppConfig { pub fn new() -> Self { Self { language: "zh".to_string(), theme: "system".to_string(), auto_refresh: true, refresh_interval: 15, auto_sync: false, sync_interval: 5, default_export_path: None, proxy: ProxyConfig::default(), antigravity_executable: None, antigravity_args: None, auto_launch: false, scheduled_warmup: ScheduledWarmupConfig::default(), quota_protection: QuotaProtectionConfig::default(), pinned_quota_models: PinnedQuotaModelsConfig::default(), circuit_breaker: CircuitBreakerConfig::default(), hidden_menu_items: Vec::new(), cloudflared: CloudflaredConfig::default(), } } } impl Default for AppConfig { fn default() -> Self { Self::new() } }