From 4292429b7853e116bd68ba649276d97c44d79070 Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:10:15 +0700 Subject: [PATCH 01/12] Add quota notification setting, adaptive refresh default and build date - session_quota_notifications_enabled gates quota depleted/restored alerts; legacy files fall back to show_notifications (migration #6) - adaptive_refresh defaults on for new installs only (migration #1) - build.rs embeds CODEXBAR_BUILD_DATE; AppInfoBridge.buildDate --- apps/desktop-tauri/src-tauri/build.rs | 34 ++++++++++ .../src-tauri/src/auto_refresh.rs | 1 + .../src-tauri/src/commands/bridge.rs | 2 + .../src-tauri/src/commands/credentials.rs | 2 + .../src-tauri/src/commands/settings.rs | 4 ++ .../src-tauri/src/commands/system.rs | 1 + apps/desktop-tauri/src/types/bridge.ts | 5 ++ rust/src/notifications.rs | 34 +++++++++- rust/src/settings.rs | 10 ++- rust/src/settings/preferences_document.rs | 1 + .../settings/preferences_document/tests.rs | 3 + rust/src/settings/raw.rs | 7 +++ rust/src/settings/tests.rs | 62 +++++++++++++++++++ 13 files changed, 164 insertions(+), 2 deletions(-) diff --git a/apps/desktop-tauri/src-tauri/build.rs b/apps/desktop-tauri/src-tauri/build.rs index d860e1e6a7..bb7145609f 100644 --- a/apps/desktop-tauri/src-tauri/build.rs +++ b/apps/desktop-tauri/src-tauri/build.rs @@ -1,3 +1,37 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + fn main() { + emit_build_date(); tauri_build::build() } + +/// Expose the UTC build date as `CODEXBAR_BUILD_DATE` (YYYY-MM-DD) for the +/// About pane. `SOURCE_DATE_EPOCH` pins it for reproducible builds. +fn emit_build_date() { + println!("cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH"); + let seconds = std::env::var("SOURCE_DATE_EPOCH") + .ok() + .and_then(|value| value.trim().parse::().ok()) + .unwrap_or_else(|| { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|elapsed| elapsed.as_secs()) + .unwrap_or(0) + }); + let (year, month, day) = civil_from_days(seconds / 86_400); + println!("cargo:rustc-env=CODEXBAR_BUILD_DATE={year:04}-{month:02}-{day:02}"); +} + +/// Days since 1970-01-01 to a proleptic Gregorian date (Howard Hinnant's algorithm). +fn civil_from_days(days: u64) -> (u64, u64, u64) { + let z = days + 719_468; + let era = z / 146_097; + let doe = z - era * 146_097; + let yoe = (doe - doe / 1_460 + doe / 36_524 - doe / 146_096) / 365; + let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); + let mp = (5 * doy + 2) / 153; + let day = doy - (153 * mp + 2) / 5 + 1; + let month = if mp < 10 { mp + 3 } else { mp - 9 }; + let year = yoe + era * 400 + u64::from(month <= 2); + (year, month, day) +} diff --git a/apps/desktop-tauri/src-tauri/src/auto_refresh.rs b/apps/desktop-tauri/src-tauri/src/auto_refresh.rs index 75128cb00d..29ccceca0a 100644 --- a/apps/desktop-tauri/src-tauri/src/auto_refresh.rs +++ b/apps/desktop-tauri/src-tauri/src/auto_refresh.rs @@ -311,6 +311,7 @@ mod tests { let mut settings = Settings { enabled_providers: ["codex".to_string()].into_iter().collect(), refresh_interval_secs: 300, + adaptive_refresh: false, ..Default::default() }; settings.set_auto_resume_after_quota_reset(ProviderId::Codex, true); diff --git a/apps/desktop-tauri/src-tauri/src/commands/bridge.rs b/apps/desktop-tauri/src-tauri/src/commands/bridge.rs index 033f24a9b9..f027155d66 100644 --- a/apps/desktop-tauri/src-tauri/src/commands/bridge.rs +++ b/apps/desktop-tauri/src-tauri/src/commands/bridge.rs @@ -830,6 +830,7 @@ pub struct SettingsSnapshot { std::collections::HashMap, predictive_pace_warning_enabled: bool, credential_expiry_notifications_enabled: bool, + session_quota_notifications_enabled: bool, show_pace: bool, tray_icon_mode: &'static str, stacked_tray_top_provider: Option, @@ -966,6 +967,7 @@ impl From for SettingsSnapshot { predictive_pace_warning_enabled: settings.predictive_pace_warning_enabled, credential_expiry_notifications_enabled: settings .credential_expiry_notifications_enabled, + session_quota_notifications_enabled: settings.session_quota_notifications_enabled, show_pace: settings.show_pace, tray_icon_mode: tray_icon_mode_label(settings.tray_icon_mode), stacked_tray_top_provider: settings.stacked_tray_top_provider, diff --git a/apps/desktop-tauri/src-tauri/src/commands/credentials.rs b/apps/desktop-tauri/src-tauri/src/commands/credentials.rs index d405d88c59..2296258e13 100644 --- a/apps/desktop-tauri/src-tauri/src/commands/credentials.rs +++ b/apps/desktop-tauri/src-tauri/src/commands/credentials.rs @@ -40,6 +40,8 @@ pub struct AppInfoBridge { pub name: String, pub version: String, pub build_number: String, + /// UTC date this binary was built (YYYY-MM-DD). + pub build_date: String, pub update_channel: String, pub tagline: String, } diff --git a/apps/desktop-tauri/src-tauri/src/commands/settings.rs b/apps/desktop-tauri/src-tauri/src/commands/settings.rs index 42cb5b2d40..b8dcf2603e 100644 --- a/apps/desktop-tauri/src-tauri/src/commands/settings.rs +++ b/apps/desktop-tauri/src-tauri/src/commands/settings.rs @@ -26,6 +26,7 @@ pub struct SettingsUpdate { Option>, pub predictive_pace_warning_enabled: Option, pub credential_expiry_notifications_enabled: Option, + pub session_quota_notifications_enabled: Option, pub show_pace: Option, pub tray_icon_mode: Option, pub stacked_tray_top_provider: Option, @@ -340,6 +341,9 @@ impl SettingsUpdate { if let Some(v) = self.credential_expiry_notifications_enabled { settings.credential_expiry_notifications_enabled = v; } + if let Some(v) = self.session_quota_notifications_enabled { + settings.session_quota_notifications_enabled = v; + } if let Some(v) = self.show_pace { settings.show_pace = v; } diff --git a/apps/desktop-tauri/src-tauri/src/commands/system.rs b/apps/desktop-tauri/src-tauri/src/commands/system.rs index 26d6a20f9d..c70e75c0f2 100644 --- a/apps/desktop-tauri/src-tauri/src/commands/system.rs +++ b/apps/desktop-tauri/src-tauri/src/commands/system.rs @@ -7,6 +7,7 @@ pub fn get_app_info() -> AppInfoBridge { name: "CodexBar".to_string(), version: env!("CARGO_PKG_VERSION").to_string(), build_number: option_env!("BUILD_NUMBER").unwrap_or("dev").to_string(), + build_date: env!("CODEXBAR_BUILD_DATE").to_string(), update_channel: update_channel_label(settings.update_channel).to_string(), tagline: "May your tokens never run out—keep agent limits in view.".to_string(), } diff --git a/apps/desktop-tauri/src/types/bridge.ts b/apps/desktop-tauri/src/types/bridge.ts index dbb975a02d..9010a37b87 100644 --- a/apps/desktop-tauri/src/types/bridge.ts +++ b/apps/desktop-tauri/src/types/bridge.ts @@ -192,6 +192,8 @@ export interface SettingsSnapshot { providerUsageThresholds?: Record; predictivePaceWarningEnabled: boolean; credentialExpiryNotificationsEnabled: boolean; + /** Absent on older shells; the UI falls back to `showNotifications`. */ + sessionQuotaNotificationsEnabled?: boolean; showPace?: boolean; trayIconMode: TrayIconMode; stackedTrayTopProvider?: string | null; @@ -323,6 +325,7 @@ export interface SettingsUpdate { providerUsageThresholds?: Record; predictivePaceWarningEnabled?: boolean; credentialExpiryNotificationsEnabled?: boolean; + sessionQuotaNotificationsEnabled?: boolean; showPace?: boolean; trayIconMode?: TrayIconMode; stackedTrayTopProvider?: string; @@ -942,6 +945,8 @@ export interface AppInfoBridge { name: string; version: string; buildNumber: string; + /** UTC build date, YYYY-MM-DD. */ + buildDate?: string; updateChannel: string; tagline: string; } diff --git a/rust/src/notifications.rs b/rust/src/notifications.rs index 3a4577ff47..a1562bf4fe 100755 --- a/rust/src/notifications.rs +++ b/rust/src/notifications.rs @@ -394,7 +394,7 @@ impl NotificationManager { current_percent: f64, settings: &Settings, ) { - if !settings.show_notifications { + if !settings.session_quota_notifications_enabled { return; } @@ -1234,4 +1234,36 @@ mod tests { manager.check_session_transition(ProviderId::Claude, "account-b", 100.0, &settings); assert!(manager.sent_notifications.contains(&depleted_b)); } + + #[test] + fn session_transition_follows_its_own_setting_not_threshold_warnings() { + let depleted = ( + ProviderId::Codex, + "account".to_string(), + "session".to_string(), + NotificationType::SessionDepleted, + ); + + // Threshold warnings off must not silence depleted/restored alerts. + let mut manager = NotificationManager::new(); + let settings = Settings { + show_notifications: false, + session_quota_notifications_enabled: true, + ..Settings::default() + }; + manager.check_session_transition(ProviderId::Codex, "account", 40.0, &settings); + manager.check_session_transition(ProviderId::Codex, "account", 100.0, &settings); + assert!(manager.sent_notifications.contains(&depleted)); + + // Turning the session-quota alerts off silences them even with warnings on. + let mut manager = NotificationManager::new(); + let settings = Settings { + show_notifications: true, + session_quota_notifications_enabled: false, + ..Settings::default() + }; + manager.check_session_transition(ProviderId::Codex, "account", 40.0, &settings); + manager.check_session_transition(ProviderId::Codex, "account", 100.0, &settings); + assert!(!manager.sent_notifications.contains(&depleted)); + } } diff --git a/rust/src/settings.rs b/rust/src/settings.rs index 7324d6afce..d920c9f3a1 100755 --- a/rust/src/settings.rs +++ b/rust/src/settings.rs @@ -201,6 +201,11 @@ pub struct Settings { #[serde(default)] pub credential_expiry_notifications_enabled: bool, + /// Notify when a 5-hour session quota is depleted and when it becomes + /// available again. Legacy files without the key inherit `show_notifications`. + #[serde(default = "default_true")] + pub session_quota_notifications_enabled: bool, + /// Show pace visualizations and forecast text in provider menu cards. #[serde(default = "default_true")] pub show_pace: bool, @@ -585,7 +590,9 @@ impl Default for Settings { preferred_currency_code: "AUTO".to_string(), enabled_providers: enabled, refresh_interval_secs: 300, // 5 minutes - adaptive_refresh: false, + // New installs use adaptive refresh; legacy files without the key + // keep the fixed interval through the field default in `RawSettings`. + adaptive_refresh: true, refresh_all_providers_on_menu_open: false, low_power_mode_preference: LowPowerModePreference::Off, start_minimized: false, @@ -611,6 +618,7 @@ impl Default for Settings { show_reset_when_exhausted: false, predictive_pace_warning_enabled: false, credential_expiry_notifications_enabled: false, + session_quota_notifications_enabled: true, show_pace: true, menu_bar_display_mode: "detailed".to_string(), // Detailed mode by default overview_layout: default_overview_layout(), diff --git a/rust/src/settings/preferences_document.rs b/rust/src/settings/preferences_document.rs index 022d584530..8efa875be0 100644 --- a/rust/src/settings/preferences_document.rs +++ b/rust/src/settings/preferences_document.rs @@ -59,6 +59,7 @@ const ALLOWED_PREFERENCES: &[(&str, Validator)] = &[ typed::, ), ("show_notifications", typed::), + ("session_quota_notifications_enabled", typed::), ("sound_enabled", typed::), ("high_usage_threshold", percent), ("critical_usage_threshold", percent), diff --git a/rust/src/settings/preferences_document/tests.rs b/rust/src/settings/preferences_document/tests.rs index 355fd677ab..b13bf1c5ad 100644 --- a/rust/src/settings/preferences_document/tests.rs +++ b/rust/src/settings/preferences_document/tests.rs @@ -33,6 +33,7 @@ fn customized_settings() -> Settings { refresh_all_providers_on_menu_open: true, low_power_mode_preference: crate::settings::LowPowerModePreference::Automatic, show_notifications: false, + session_quota_notifications_enabled: false, sound_enabled: false, high_usage_threshold: 55.5, critical_usage_threshold: 80.0, @@ -136,6 +137,8 @@ fn round_trips_every_allowlisted_preference() { assert_eq!(again, exported); assert_eq!(target.window_scale_percent, 150); assert_eq!(target.ui_language, Language::Japanese); + // Portable like the Mac PreferencesDocument: depleted/restored alerts travel. + assert!(!target.session_quota_notifications_enabled); assert_eq!( target.cost_reporting_period, CostReportingPeriod::MonthToDate diff --git a/rust/src/settings/raw.rs b/rust/src/settings/raw.rs index 246cd1fd4e..b93a016836 100644 --- a/rust/src/settings/raw.rs +++ b/rust/src/settings/raw.rs @@ -133,6 +133,9 @@ pub(super) struct RawSettings { show_reset_when_exhausted: bool, predictive_pace_warning_enabled: bool, credential_expiry_notifications_enabled: bool, + /// `None` when the file predates the key; it then inherits `show_notifications`. + #[serde(default)] + session_quota_notifications_enabled: Option, #[serde(default = "default_true")] show_pace: bool, menu_bar_display_mode: String, @@ -316,6 +319,7 @@ impl Default for RawSettings { show_reset_when_exhausted: s.show_reset_when_exhausted, predictive_pace_warning_enabled: s.predictive_pace_warning_enabled, credential_expiry_notifications_enabled: s.credential_expiry_notifications_enabled, + session_quota_notifications_enabled: Some(s.session_quota_notifications_enabled), show_pace: s.show_pace, menu_bar_display_mode: s.menu_bar_display_mode, overview_layout: s.overview_layout, @@ -662,6 +666,9 @@ impl From for Settings { show_reset_when_exhausted: raw.show_reset_when_exhausted, predictive_pace_warning_enabled: raw.predictive_pace_warning_enabled, credential_expiry_notifications_enabled: raw.credential_expiry_notifications_enabled, + session_quota_notifications_enabled: raw + .session_quota_notifications_enabled + .unwrap_or(raw.show_notifications), show_pace: raw.show_pace, menu_bar_display_mode: raw.menu_bar_display_mode, overview_layout: normalize_overview_layout(&raw.overview_layout), diff --git a/rust/src/settings/tests.rs b/rust/src/settings/tests.rs index 2d7d051c0b..33ca17aa0e 100644 --- a/rust/src/settings/tests.rs +++ b/rust/src/settings/tests.rs @@ -21,6 +21,8 @@ fn test_settings_default() { assert!(!settings.show_reset_when_exhausted); assert!(!settings.predictive_pace_warning_enabled); assert!(!settings.credential_expiry_notifications_enabled); + assert!(settings.session_quota_notifications_enabled); + assert!(settings.adaptive_refresh); assert!(!settings.float_bar_show_cost); assert!(!settings.tray_panel_always_on_top); assert_eq!(settings.overview_layout, "detailed"); @@ -1538,3 +1540,63 @@ fn show_as_used_keeps_a_saved_used_preference() { assert!(Settings::load_from_path(Some(&path)).show_as_used); } + +/// Migration #1: a file written before the session-quota key existed keeps the +/// behaviour it had, when depleted/restored alerts followed `show_notifications`. +#[test] +fn session_quota_notifications_inherit_show_notifications_from_legacy_files() { + let off: Settings = + serde_json::from_str(r#"{ "enabled_providers": [], "show_notifications": false }"#) + .expect("deserialize legacy settings with notifications off"); + assert!(!off.session_quota_notifications_enabled); + + let on: Settings = + serde_json::from_str(r#"{ "enabled_providers": [], "show_notifications": true }"#) + .expect("deserialize legacy settings with notifications on"); + assert!(on.session_quota_notifications_enabled); + + // Once the key is stored it wins over `show_notifications` in both directions. + let explicit_on: Settings = serde_json::from_str( + r#"{ "enabled_providers": [], "show_notifications": false, + "session_quota_notifications_enabled": true }"#, + ) + .expect("deserialize explicit session quota opt-in"); + assert!(explicit_on.session_quota_notifications_enabled); + assert!(!explicit_on.show_notifications); + + let explicit_off: Settings = serde_json::from_str( + r#"{ "enabled_providers": [], "show_notifications": true, + "session_quota_notifications_enabled": false }"#, + ) + .expect("deserialize explicit session quota opt-out"); + assert!(!explicit_off.session_quota_notifications_enabled); + + let round_trip: Settings = + serde_json::from_str(&serde_json::to_string(&explicit_on).expect("serialize")) + .expect("deserialize round trip"); + assert!(round_trip.session_quota_notifications_enabled); + assert!(!round_trip.show_notifications); +} + +/// Migration #6: adaptive refresh is the default for new installs only. +#[test] +fn adaptive_refresh_defaults_on_for_new_installs_only() { + // No settings file: a new install. + assert!(Settings::load_from_path(None).adaptive_refresh); + let dir = tempfile::tempdir().expect("temp dir"); + let missing = dir.path().join("settings.json"); + assert!(Settings::load_from_path(Some(&missing)).adaptive_refresh); + + // An existing file without the key keeps the fixed interval it ran with. + std::fs::write(&missing, "{}").expect("write legacy settings"); + let legacy = Settings::load_from_path(Some(&missing)); + assert!(!legacy.adaptive_refresh); + assert_eq!(legacy.refresh_interval_secs, 300); + + let explicit: Settings = serde_json::from_str(r#"{ "adaptive_refresh": true }"#) + .expect("deserialize explicit adaptive refresh"); + assert!(explicit.adaptive_refresh); + let explicit_off: Settings = serde_json::from_str(r#"{ "adaptive_refresh": false }"#) + .expect("deserialize explicit fixed refresh"); + assert!(!explicit_off.adaptive_refresh); +} From 798f215da470587e1b5f25ff2fe33a2406ad6249 Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:14:37 +0700 Subject: [PATCH 02/12] Add Mac settings pane strings for all locales New keys for the grouped General, Notifications, Advanced and About panes; Mac labels for renamed rows; honest Credential Manager helper text. --- apps/desktop-tauri/src/i18n/keys.ts | 19 ++++++++ rust/src/locale.rs | 19 ++++++++ rust/src/locale/en-US.ftl | 73 ++++++++++++++++++----------- rust/src/locale/es-MX.ftl | 31 +++++++++--- rust/src/locale/ja-JP.ftl | 31 +++++++++--- rust/src/locale/ko-KR.ftl | 31 +++++++++--- rust/src/locale/pt-BR.ftl | 31 +++++++++--- rust/src/locale/ru-RU.ftl | 31 +++++++++--- rust/src/locale/tests.rs | 4 +- rust/src/locale/tr-TR.ftl | 31 +++++++++--- rust/src/locale/uk-UA.ftl | 31 +++++++++--- rust/src/locale/zh-CN.ftl | 31 +++++++++--- rust/src/locale/zh-TW.ftl | 31 +++++++++--- 13 files changed, 311 insertions(+), 83 deletions(-) diff --git a/apps/desktop-tauri/src/i18n/keys.ts b/apps/desktop-tauri/src/i18n/keys.ts index bca1f5588c..e9820a31cc 100644 --- a/apps/desktop-tauri/src/i18n/keys.ts +++ b/apps/desktop-tauri/src/i18n/keys.ts @@ -17,6 +17,7 @@ export const ALL_LOCALE_KEYS = [ "TabAbout", "TabShortcuts", "InterfaceLanguage", + "GeneralLanguageHelper", "StartupSettings", "StartAtLogin", "StartMinimized", @@ -227,6 +228,7 @@ export const ALL_LOCALE_KEYS = [ "MenuAbout", "MenuAboutVersion", "MenuQuit", + "QuitCodexBar", "TrayMenuSwitchAccount", "TrayMenuUsageDashboard", "TrayMenuStatusPage", @@ -579,6 +581,8 @@ export const ALL_LOCALE_KEYS = [ "AgentSessionsLoading", "AgentSessionsEmpty", "UpdatesTitle", + "AboutBuilt", + "SectionLinks", "UpdateChannelChoice", "UpdateChannelChoiceHelper", "AutoDownloadUpdates", @@ -595,6 +599,7 @@ export const ALL_LOCALE_KEYS = [ "SelectProvider", "RefreshInterval30Sec", "RefreshInterval1Min", + "RefreshInterval2Min", "RefreshInterval5Min", "RefreshInterval10Min", "BrowserCookiesTitle", @@ -633,7 +638,17 @@ export const ALL_LOCALE_KEYS = [ "UpdateDownloadingMessage", "TabTokenAccounts", "SectionRefresh", + "SectionRefreshing", + "RefreshManualFooter", "SectionNotifications", + "SectionAlerts", + "SessionQuotaNotifications", + "SessionQuotaNotificationsHelper", + "WarningAtPercentUsed", + "CriticalAtPercentUsed", + "ThresholdsCaption", + "ThresholdOverridesTitle", + "ThresholdOverridesCaption", "SectionUsageThresholds", "SectionKeyboard", "SectionUsageRendering", @@ -667,6 +682,9 @@ export const ALL_LOCALE_KEYS = [ "PreferencesTransferCaption", "PreferencesExportButton", "PreferencesImportButton", + "PreferencesTransferFooter", + "SwitcherShortcutsOpenButton", + "DialogDone", "PreferencesExportSuccess", "PreferencesImportSuccess", "ShortcutRecordButton", @@ -735,6 +753,7 @@ export const ALL_LOCALE_KEYS = [ "AvoidKeychainPromptsHelper", "DisableAllKeychainLabel", "DisableAllKeychainHelper", + "CredentialAccessFooter", "SectionTheme", "ThemeLabel", "ThemeHelper", diff --git a/rust/src/locale.rs b/rust/src/locale.rs index 0f3904ef59..7b2c6468f4 100644 --- a/rust/src/locale.rs +++ b/rust/src/locale.rs @@ -203,6 +203,7 @@ locale_keys! { // General settings (Preferences) InterfaceLanguage, + GeneralLanguageHelper, PreferredCurrencyLabel, PreferredCurrencyHelper, StartupSettings, @@ -447,6 +448,7 @@ locale_keys! { MenuAbout, MenuAboutVersion, MenuQuit, + QuitCodexBar, TrayMenuSwitchAccount, TrayMenuUsageDashboard, TrayMenuStatusPage, @@ -829,6 +831,8 @@ locale_keys! { AgentSessionsLoading, AgentSessionsEmpty, UpdatesTitle, + AboutBuilt, + SectionLinks, UpdateChannelChoice, UpdateChannelChoiceHelper, AutoDownloadUpdates, @@ -851,6 +855,7 @@ locale_keys! { // Refresh interval labels RefreshInterval30Sec, RefreshInterval1Min, + RefreshInterval2Min, RefreshInterval5Min, RefreshInterval10Min, @@ -901,7 +906,17 @@ locale_keys! { // Tauri desktop shell — Settings section headings TabTokenAccounts, SectionRefresh, + SectionRefreshing, + RefreshManualFooter, SectionNotifications, + SectionAlerts, + SessionQuotaNotifications, + SessionQuotaNotificationsHelper, + WarningAtPercentUsed, + CriticalAtPercentUsed, + ThresholdsCaption, + ThresholdOverridesTitle, + ThresholdOverridesCaption, SectionUsageThresholds, SectionKeyboard, SectionUsageRendering, @@ -935,6 +950,9 @@ locale_keys! { PreferencesTransferCaption, PreferencesExportButton, PreferencesImportButton, + PreferencesTransferFooter, + SwitcherShortcutsOpenButton, + DialogDone, PreferencesExportSuccess, PreferencesImportSuccess, ShortcutRecordButton, @@ -1006,6 +1024,7 @@ locale_keys! { AvoidKeychainPromptsHelper, DisableAllKeychainLabel, DisableAllKeychainHelper, + CredentialAccessFooter, // Tauri desktop shell — Theme (Phase 12) SectionTheme, ThemeLabel, diff --git a/rust/src/locale/en-US.ftl b/rust/src/locale/en-US.ftl index 06e082deda..17d9407d3b 100644 --- a/rust/src/locale/en-US.ftl +++ b/rust/src/locale/en-US.ftl @@ -9,20 +9,21 @@ TabUsageSpend = Usage & Spend TabAdvanced = Advanced TabAbout = About TabShortcuts = Shortcuts -InterfaceLanguage = Interface Language -PreferredCurrencyLabel = Preferred currency -PreferredCurrencyHelper = Convert supported provider costs for display. Auto keeps each provider's source currency. +InterfaceLanguage = Language +GeneralLanguageHelper = Change the display language. +PreferredCurrencyLabel = Preferred Currency +PreferredCurrencyHelper = Currency for cost estimates and spend metrics. Uses live exchange rates. Auto keeps each provider's source currency. StartupSettings = System -StartAtLogin = Start at Login -StartMinimized = Start Minimized +StartAtLogin = Start at login +StartMinimized = Start minimized StartAtLoginHelper = Login automatically after system startup StartMinimizedHelper = Start minimized to system tray -ShowNotificationsHelper = Alert when usage thresholds are reached +ShowNotificationsHelper = Warns when session or weekly usage crosses the thresholds below. Turning it off mutes every alert except Quota depleted & restored. SoundEnabledHelper = Play sound when thresholds are reached HighUsageThresholdHelper = Show warning at this usage level CriticalUsageThresholdHelper = Show critical alert at this level -ShowNotifications = Show Notifications -SoundEnabled = Sound Alerts +ShowNotifications = Threshold warnings +SoundEnabled = Play notification sound NotificationSoundTheme = Default sound set NotificationSoundThemeHelper = Used when no custom WAV file is assigned. NotificationSoundThemeWindows = Windows system sounds @@ -49,8 +50,8 @@ HighUsageThreshold = High Usage Threshold HighUsageAlert = High Usage Alert CriticalUsageThreshold = Critical Usage Threshold CriticalUsageAlert = Critical Alert -PredictivePaceWarnings = Predictive Pace Warnings -PredictivePaceWarningsHelper = Alert when Codex or Claude usage is likely to run out before reset +PredictivePaceWarnings = Pace warnings +PredictivePaceWarningsHelper = Warns for Codex and Claude when session or weekly pace may run out before reset. PredictivePaceWarningTitle = { "{}" } { "{}" } pace warning PredictivePaceWarningBody = Quota may run out in { "{}" } NotificationHighUsageTitle = High Usage Warning @@ -68,8 +69,8 @@ NotificationSessionRestoredBody = { "{}" } session restored. Session quota is av NotificationWindowSession = session NotificationWindowWeekly = weekly NotificationWindowUsage = usage -CredentialExpiryNotifications = Credential Expiry Alerts -CredentialExpiryNotificationsHelper = Alert once when a provider account needs to sign in again +CredentialExpiryNotifications = Credential expiry +CredentialExpiryNotificationsHelper = Notify once when a provider account needs you to sign in again. CredentialExpiryTitle = { "{}" } needs sign-in CredentialExpiryBody = Open CodexBar to review the account error and sign in again. UsageDisplay = Usage Display @@ -214,6 +215,7 @@ MenuSettings = Settings... MenuAbout = About CodexBar MenuAboutVersion = About CodexBar (v{ "{}" }) MenuQuit = Quit +QuitCodexBar = Quit CodexBar TrayMenuSwitchAccount = Switch Account... TrayMenuUsageDashboard = Usage Dashboard TrayMenuStatusPage = Status Page @@ -375,8 +377,8 @@ OAuth = OAuth Api = API Web = Web PrivacyTitle = Privacy -HidePersonalInfo = Hide Personal Info -HidePersonalInfoHelper = Mask emails and account names (good for streaming) +HidePersonalInfo = Hide personal information +HidePersonalInfoHelper = Obscure email addresses in the tray menu and panel. Also hide cost-history project/source names and paths without changing stored data. SectionLocalIntegrations = Local integrations PowerToysPipeLabel = PowerToys status pipe PowerToysPipeHelper = Expose cached provider status and local usage totals to PowerToys Command Palette through a named pipe. No emails or plans are included. Restart required. @@ -447,7 +449,9 @@ AgentSessionsSshHostsHelper = Comma-separated SSH targets. Connections use non-i AgentSessionsLoading = Looking for agent sessions... AgentSessionsEmpty = No active agent sessions. UpdatesTitle = Updates -UpdateChannelChoice = Update Channel +AboutBuilt = Built { "{}" } +SectionLinks = Links +UpdateChannelChoice = Update channel UpdateChannelChoiceHelper = Choose between stable and beta preview versions AutoDownloadUpdates = Check for updates automatically AutoDownloadUpdatesHelper = Download installer updates in the background when a new release is found @@ -463,6 +467,7 @@ ShortcutHintPlaceholder = e.g., Ctrl+Shift+U SelectProvider = Select a provider RefreshInterval30Sec = 30 sec RefreshInterval1Min = 1 min +RefreshInterval2Min = 2 min RefreshInterval5Min = 5 min RefreshInterval10Min = 10 min BrowserCookiesTitle = Browser Cookies @@ -473,7 +478,7 @@ AutoRefreshInterval = Auto-refresh interval AboutDescription = A Windows port of the original macOS version. AboutDescriptionLine2 = Track AI provider usage in your system tray. ViewOnGitHub = → View on GitHub -SubmitIssue = → Submit an Issue +SubmitIssue = Submit an Issue MaintainedBy = Maintained by CodexBar contributors CommitLabel = Commit BuildDateLabel = Built @@ -501,9 +506,19 @@ UpdateFailedMessage = Update failed: { "{}" } UpdateDownloadingMessage = Downloading { "{}" } ({ "{:.0}" }%) TabTokenAccounts = Tokens SectionRefresh = Automation +SectionRefreshing = Refreshing +RefreshManualFooter = Auto-refresh is off; use Refresh All in the tray menu. SectionNotifications = Notifications +SectionAlerts = Alerts +SessionQuotaNotifications = Quota depleted & restored +SessionQuotaNotificationsHelper = Notifies when a provider's session quota hits 0% left and when it becomes available again. +WarningAtPercentUsed = Warning at % used +CriticalAtPercentUsed = Critical at % used +ThresholdsCaption = Used percentages for session and weekly windows unless a provider overrides them. +ThresholdOverridesTitle = Provider thresholds +ThresholdOverridesCaption = Codex and Claude can use their own warning and critical % used. Leave a field empty to use the value above it. SectionUsageThresholds = Usage Thresholds -SectionKeyboard = Keyboard +SectionKeyboard = Keyboard shortcut SectionUsageRendering = Usage rendering SectionTime = Time SectionLanguage = Language @@ -518,7 +533,7 @@ SectionSavedAccounts = Saved Accounts SectionAddAccount = Add Account RefreshIntervalLabel = Refresh interval RefreshIntervalHelper = Seconds between automatic provider refreshes (0 = manual). -RefreshAllProvidersOnMenuOpen = Refresh on menu open +RefreshAllProvidersOnMenuOpen = Refresh when the menu opens RefreshAllProvidersOnMenuOpenHelper = Force-refresh enabled providers whenever the tray menu opens. LowPowerMode = Low Power Mode LowPowerModeHelper = When on, limit automatic background refresh to once every 30 minutes. Manual refresh stays immediate. Automatic follows Windows Battery Saver. @@ -527,12 +542,15 @@ LowPowerModeOn = On LowPowerModeAutomatic = Automatic HighUsageWarningHelper = Show a warning when usage exceeds this percentage. CriticalUsageWarningHelper = Show a critical alert when usage exceeds this percentage. -GlobalShortcutFieldLabel = Global shortcut +GlobalShortcutFieldLabel = Open menu GlobalShortcutToggleHelper = Key combination to toggle the tray panel. SectionPreferencesTransfer = Portable preferences PreferencesTransferCaption = Copy display, refresh, notification and provider choices to another PC. API keys, cookies, folders and hosts are never included. -PreferencesExportButton = Export preferences -PreferencesImportButton = Import preferences +PreferencesExportButton = Export Preferences… +PreferencesImportButton = Import Preferences… +PreferencesTransferFooter = Save display and notification preferences to a file you can import on another PC. Accounts, credentials, login, local paths and consent stay on this PC. +SwitcherShortcutsOpenButton = Provider Switcher Shortcuts… +DialogDone = Done PreferencesExportSuccess = Preferences exported. PreferencesImportSuccess = Preferences imported. ShortcutRecordButton = Record @@ -547,7 +565,7 @@ SwitcherShortcutNext = Next provider SwitcherShortcutSelect = Go to item SwitcherShortcutNone = Disabled SwitcherShortcutRecordingHint = Press a key or combination. Esc cancels, Backspace disables. -SwitcherShortcutReset = Reset to defaults +SwitcherShortcutReset = Restore Defaults SwitcherShortcutErrorUnknown = That action is not a provider switcher shortcut. SwitcherShortcutErrorDuplicate = That key is already assigned to another action. SwitcherShortcutErrorReserved = That key is reserved. Letters, digits and comma need Ctrl or Alt; Ctrl+R, Ctrl+Q, Ctrl+, and Ctrl+W are taken. @@ -599,12 +617,13 @@ CodexLogPathsLabel = Codex log paths CodexLogPathsHelper = A .codex root is accepted; it will scan the sessions folder inside it. AvoidKeychainPromptsLabel = Avoid keychain prompts (Claude) AvoidKeychainPromptsHelper = Skip keychain credential reads for Claude to prevent OS permission dialogs. -DisableAllKeychainLabel = Disable all keychain access -DisableAllKeychainHelper = Turn off credential/keychain reads for all providers. Also enables the Claude option above. +DisableAllKeychainLabel = Disable Credential Manager access +DisableAllKeychainHelper = Not enforced on Windows yet: CodexBar still uses Credential Manager, DPAPI and browser cookies. The choice is saved for when blocking ships. +CredentialAccessFooter = While Credential Manager access is disabled, the Claude keychain-prompt option stays locked. SectionTheme = Appearance ThemeLabel = Theme ThemeHelper = Auto follows your system color scheme. Light and Dark override it. -ThemeAutoOption = Auto (system) +ThemeAutoOption = Automatic ThemeLightOption = Light ThemeDarkOption = Dark SettingsStatusSaving = Saving… @@ -853,8 +872,8 @@ LoadingShellContract = Loading shell contract… LoadingShellContractHint = Waiting for the Rust bridge to describe providers, surfaces, and settings. RefreshIntervalManual = Manual RefreshIntervalAdaptive = Adaptive -RefreshInterval15Min = 15 minutes -RefreshInterval30Min = 30 minutes +RefreshInterval15Min = 15 min +RefreshInterval30Min = 30 min RefreshInterval1Hour = 1 hour ProviderNameCodex = Codex ProviderNameClaude = Claude diff --git a/rust/src/locale/es-MX.ftl b/rust/src/locale/es-MX.ftl index efbd5e4c4f..3826e47534 100644 --- a/rust/src/locale/es-MX.ftl +++ b/rust/src/locale/es-MX.ftl @@ -10,6 +10,7 @@ TabAdvanced = Avanzado TabAbout = Acerca de TabShortcuts = Atajos InterfaceLanguage = Idioma de la interfaz +GeneralLanguageHelper = Cambia el idioma de la interfaz. PreferredCurrencyLabel = Moneda preferida PreferredCurrencyHelper = Convierte los costos compatibles de los proveedores para mostrarlos. Auto conserva la moneda de origen de cada proveedor. StartupSettings = Sistema @@ -17,11 +18,11 @@ StartAtLogin = Iniciar al arrancar StartMinimized = Iniciar minimizado StartAtLoginHelper = Iniciar sesión automáticamente al arrancar el sistema StartMinimizedHelper = Iniciar minimizado en la bandeja del sistema -ShowNotificationsHelper = Alertar cuando se alcancen los umbrales de uso +ShowNotificationsHelper = Avisa cuando el uso de sesión o semanal supera los umbrales de abajo. Al desactivarlo se silencian todas las alertas excepto Cuota agotada y restablecida. SoundEnabledHelper = Reproducir sonido cuando se alcancen los umbrales HighUsageThresholdHelper = Mostrar advertencia en este nivel de uso CriticalUsageThresholdHelper = Mostrar alerta crítica en este nivel -ShowNotifications = Mostrar notificaciones +ShowNotifications = Avisos de umbral SoundEnabled = Alertas sonoras NotificationSoundTheme = Conjunto de sonidos predeterminado NotificationSoundThemeHelper = Se usa cuando no se asigna un archivo WAV personalizado. @@ -187,6 +188,7 @@ MenuSettings = Configuración... MenuAbout = Acerca de CodexBar MenuAboutVersion = Acerca de CodexBar (v{ "{}" }) MenuQuit = Salir +QuitCodexBar = Salir de CodexBar TrayMenuSwitchAccount = Cambiar cuenta... TrayMenuUsageDashboard = Panel de uso TrayMenuStatusPage = Página de estado @@ -349,7 +351,7 @@ Api = API Web = Web PrivacyTitle = Privacidad HidePersonalInfo = Ocultar información personal -HidePersonalInfoHelper = Ocultar correos y nombres de cuenta (útil para transmisiones) +HidePersonalInfoHelper = Oculta las direcciones de correo en el menú de la bandeja y el panel. También oculta nombres de proyectos/orígenes y rutas del historial de costos sin cambiar los datos guardados. SectionLocalIntegrations = Integraciones locales PowerToysPipeLabel = Canal de estado de PowerToys PowerToysPipeHelper = Exponer el estado de proveedores en cache y totales de uso local a PowerToys Command Palette mediante un canal con nombre. No incluye correos ni planes. Requiere reiniciar. @@ -420,6 +422,8 @@ AgentSessionsSshHostsHelper = Destinos SSH separados por comas. Las conexiones u AgentSessionsLoading = Buscando sesiones de agentes... AgentSessionsEmpty = No hay sesiones de agentes activas. UpdatesTitle = Actualizaciones +AboutBuilt = Compilado el { "{}" } +SectionLinks = Enlaces UpdateChannelChoice = Canal de actualización UpdateChannelChoiceHelper = Elegir entre versiones estables y versiones beta de prueba AutoDownloadUpdates = Buscar actualizaciones automáticamente @@ -436,6 +440,7 @@ ShortcutHintPlaceholder = p. ej., Ctrl+Shift+U SelectProvider = Seleccionar un proveedor RefreshInterval30Sec = 30 seg RefreshInterval1Min = 1 min +RefreshInterval2Min = 2 min RefreshInterval5Min = 5 min RefreshInterval10Min = 10 min BrowserCookiesTitle = Cookies del navegador @@ -446,7 +451,7 @@ AutoRefreshInterval = Intervalo de actualización automática AboutDescription = Una adaptación para Windows de la versión original de macOS. AboutDescriptionLine2 = Rastree el uso de proveedores de IA en su bandeja del sistema. ViewOnGitHub = → Ver en GitHub -SubmitIssue = → Reportar un problema +SubmitIssue = Reportar un problema MaintainedBy = Mantenido por colaboradores de CodexBar CommitLabel = Commit BuildDateLabel = Compilado @@ -474,7 +479,17 @@ UpdateFailedMessage = Actualización fallida: { "{}" } UpdateDownloadingMessage = Descargando { "{}" } ({ "{:.0}" }%) TabTokenAccounts = Tokens SectionRefresh = Automatización +SectionRefreshing = Actualización +RefreshManualFooter = La actualización automática está desactivada; usa Actualizar todo en el menú de la bandeja. SectionNotifications = Notificaciones +SectionAlerts = Alertas +SessionQuotaNotifications = Cuota agotada y restablecida +SessionQuotaNotificationsHelper = Avisa cuando la cuota de sesión de un proveedor llega a 0 % restante y cuando vuelve a estar disponible. +WarningAtPercentUsed = Advertencia al % usado +CriticalAtPercentUsed = Crítico al % usado +ThresholdsCaption = Porcentajes usados para las ventanas de sesión y semanales, salvo que un proveedor los reemplace. +ThresholdOverridesTitle = Umbrales por proveedor +ThresholdOverridesCaption = Codex y Claude pueden usar su propio % usado de advertencia y crítico. Deja un campo vacío para usar el valor superior. SectionUsageThresholds = Umbrales de uso SectionKeyboard = Teclado SectionUsageRendering = Representación de uso @@ -551,6 +566,9 @@ SectionPreferencesTransfer = Preferencias portátiles PreferencesTransferCaption = Copia las opciones de visualización, actualización, notificaciones y proveedores a otra PC. Nunca se incluyen claves API, cookies, carpetas ni hosts. PreferencesExportButton = Exportar preferencias PreferencesImportButton = Importar preferencias +PreferencesTransferFooter = Guarda las preferencias de visualización y notificaciones en un archivo que puedes importar en otro PC. Las cuentas, credenciales, el inicio de sesión, las rutas locales y los consentimientos se quedan en este PC. +SwitcherShortcutsOpenButton = Atajos del selector de proveedores… +DialogDone = Listo PreferencesExportSuccess = Preferencias exportadas. PreferencesImportSuccess = Preferencias importadas. ShortcutRecordButton = Grabar @@ -617,8 +635,9 @@ CodexLogPathsLabel = Rutas de logs de Codex CodexLogPathsHelper = Se acepta una raíz .codex; se escaneará la carpeta sessions dentro de ella. AvoidKeychainPromptsLabel = Evitar avisos de llavero (Claude) AvoidKeychainPromptsHelper = Omitir lecturas de credenciales de llavero para Claude para evitar diálogos de permiso del SO. -DisableAllKeychainLabel = Deshabilitar todo acceso al llavero -DisableAllKeychainHelper = Desactivar lecturas de credenciales/llavero para todos los proveedores. También habilita la opción de Claude anterior. +DisableAllKeychainLabel = Deshabilitar el acceso al Administrador de credenciales +DisableAllKeychainHelper = Aún no se aplica en Windows: CodexBar sigue usando el Administrador de credenciales, DPAPI y las cookies del navegador. La opción se guarda para cuando llegue el bloqueo. +CredentialAccessFooter = Mientras el acceso al Administrador de credenciales esté deshabilitado, la opción de avisos del llavero de Claude queda bloqueada. SectionTheme = Apariencia ThemeLabel = Tema ThemeHelper = Auto sigue el esquema de color de su sistema. Claro y oscuro lo anulan. diff --git a/rust/src/locale/ja-JP.ftl b/rust/src/locale/ja-JP.ftl index b09af73828..fc81af99cf 100644 --- a/rust/src/locale/ja-JP.ftl +++ b/rust/src/locale/ja-JP.ftl @@ -10,6 +10,7 @@ TabAdvanced = 詳細 TabAbout = 情報 TabShortcuts = ショートカット InterfaceLanguage = 表示言語 +GeneralLanguageHelper = 表示言語を変更します。 PreferredCurrencyLabel = 優先通貨 PreferredCurrencyHelper = 対応プロバイダーのコストを換算して表示します。「自動」は各プロバイダー本来の通貨を維持します。 StartupSettings = システム @@ -17,11 +18,11 @@ StartAtLogin = ログイン時に起動 StartMinimized = 最小化して起動 StartAtLoginHelper = システム起動後に自動的にログインします StartMinimizedHelper = システムトレイに最小化して起動します -ShowNotificationsHelper = 使用量がしきい値に達したときに通知する +ShowNotificationsHelper = セッションまたは週間の使用量が下のしきい値を超えたときに警告します。オフにすると「クォータの枯渇と回復」以外のすべてのアラートが止まります。 SoundEnabledHelper = しきい値に達したときにサウンドを鳴らす HighUsageThresholdHelper = この使用量で警告を表示 CriticalUsageThresholdHelper = このレベルで重大なアラートを表示 -ShowNotifications = 通知を表示 +ShowNotifications = しきい値の警告 SoundEnabled = サウンド通知 NotificationSoundTheme = 既定の通知音セット NotificationSoundThemeHelper = 独自の WAV ファイルを指定していない通知に使用します。 @@ -187,6 +188,7 @@ MenuSettings = 設定... MenuAbout = CodexBar について MenuAboutVersion = CodexBar について (v{ "{}" }) MenuQuit = 終了 +QuitCodexBar = CodexBar を終了 TrayMenuSwitchAccount = アカウントを切り替え… TrayMenuUsageDashboard = 使用量ダッシュボード TrayMenuStatusPage = ステータスページ @@ -349,7 +351,7 @@ Api = API Web = Web PrivacyTitle = プライバシー HidePersonalInfo = 個人情報を隠す -HidePersonalInfoHelper = メールとアカウント名をマスク(配信時に便利) +HidePersonalInfoHelper = トレイメニューとパネルのメールアドレスを隠します。保存データは変えずに、コスト履歴のプロジェクト名・ソース名とパスも隠します。 SectionLocalIntegrations = ローカル連携 PowerToysPipeLabel = PowerToys ステータスパイプ PowerToysPipeHelper = 名前付きパイプ経由で、キャッシュ済みのプロバイダーステータスとローカル使用量の合計を PowerToys Command Palette に公開します。メールやプランは含まれません。再起動が必要です。 @@ -420,6 +422,8 @@ AgentSessionsSshHostsHelper = カンマ区切りの SSH ターゲット。接続 AgentSessionsLoading = エージェントセッションを検索中… AgentSessionsEmpty = アクティブなエージェントセッションはありません。 UpdatesTitle = アップデート +AboutBuilt = ビルド日 { "{}" } +SectionLinks = リンク UpdateChannelChoice = アップデートチャネル UpdateChannelChoiceHelper = 安定版とベータプレビュー版を選択 AutoDownloadUpdates = 更新を自動的に確認 @@ -436,6 +440,7 @@ ShortcutHintPlaceholder = 例:Ctrl+Shift+U SelectProvider = プロバイダーを選択 RefreshInterval30Sec = 30秒 RefreshInterval1Min = 1分 +RefreshInterval2Min = 2分 RefreshInterval5Min = 5分 RefreshInterval10Min = 10分 BrowserCookiesTitle = ブラウザ Cookie @@ -446,7 +451,7 @@ AutoRefreshInterval = 自動更新間隔 AboutDescription = オリジナル macOS 版の Windows 移植版。 AboutDescriptionLine2 = システムトレイで AI プロバイダーの使用量を追跡。 ViewOnGitHub = → GitHub で見る -SubmitIssue = → 問題を報告 +SubmitIssue = 問題を報告 MaintainedBy = CodexBar コントリビューターがメンテナンス CommitLabel = コミット BuildDateLabel = ビルド日時 @@ -474,7 +479,17 @@ UpdateFailedMessage = 更新に失敗: { "{}" } UpdateDownloadingMessage = ダウンロード中 { "{}" } ({ "{:.0}" }%) TabTokenAccounts = トークン SectionRefresh = 自動化 +SectionRefreshing = 更新 +RefreshManualFooter = 自動更新はオフです。トレイメニューの「すべて更新」を使ってください。 SectionNotifications = 通知 +SectionAlerts = アラート +SessionQuotaNotifications = クォータの枯渇と回復 +SessionQuotaNotificationsHelper = プロバイダーのセッションクォータの残りが 0% になったときと、再び使えるようになったときに通知します。 +WarningAtPercentUsed = 警告(使用率 %) +CriticalAtPercentUsed = 重大(使用率 %) +ThresholdsCaption = プロバイダーで上書きしない限り、セッションと週間の枠に使う使用率です。 +ThresholdOverridesTitle = プロバイダー別のしきい値 +ThresholdOverridesCaption = Codex と Claude は独自の警告・重大の使用率を設定できます。空欄にすると上位の値を使います。 SectionUsageThresholds = 使用量しきい値 SectionKeyboard = キーボード SectionUsageRendering = 使用量の表示 @@ -506,6 +521,9 @@ SectionPreferencesTransfer = 設定の持ち運び PreferencesTransferCaption = 表示、更新、通知、プロバイダーの設定を別の PC にコピーします。API キー、Cookie、フォルダー、ホストは含まれません。 PreferencesExportButton = 設定をエクスポート PreferencesImportButton = 設定をインポート +PreferencesTransferFooter = 表示と通知の設定をファイルに保存し、別の PC で読み込めます。アカウント、認証情報、ログイン時の起動、ローカルパス、同意はこの PC に残ります。 +SwitcherShortcutsOpenButton = プロバイダー切り替えショートカット… +DialogDone = 完了 PreferencesExportSuccess = 設定をエクスポートしました。 PreferencesImportSuccess = 設定をインポートしました。 ShortcutRecordButton = 録音 @@ -572,8 +590,9 @@ CodexLogPathsLabel = Codex ログパス CodexLogPathsHelper = .codex ルートも指定できます。その中の sessions フォルダをスキャンします。 AvoidKeychainPromptsLabel = キーチェーン要求を出さない(Claude) AvoidKeychainPromptsHelper = Claude のキーチェーン認証情報の読み取りをスキップし、OS の権限ダイアログを防ぐ。 -DisableAllKeychainLabel = すべてのキーチェーンアクセスを無効化 -DisableAllKeychainHelper = 全プロバイダーの認証情報/キーチェーン読み取りをオフ。上の Claude オプションも有効化。 +DisableAllKeychainLabel = 資格情報マネージャーへのアクセスを無効化 +DisableAllKeychainHelper = Windows ではまだ適用されません。CodexBar は引き続き資格情報マネージャー、DPAPI、ブラウザーの Cookie を使います。この選択はブロック機能の提供に備えて保存されます。 +CredentialAccessFooter = 資格情報マネージャーへのアクセスを無効にしている間、Claude のキーチェーンプロンプトのオプションはロックされます。 SectionTheme = 外観 ThemeLabel = テーマ ThemeHelper = 自動はシステムの配色に従います。ライトまたはダークで上書きできます。 diff --git a/rust/src/locale/ko-KR.ftl b/rust/src/locale/ko-KR.ftl index b71361f19d..9270f6fa1d 100644 --- a/rust/src/locale/ko-KR.ftl +++ b/rust/src/locale/ko-KR.ftl @@ -10,6 +10,7 @@ TabAdvanced = 고급 TabAbout = 정보 TabShortcuts = 단축키 InterfaceLanguage = 인터페이스 언어 +GeneralLanguageHelper = 표시 언어를 변경합니다. PreferredCurrencyLabel = 선호 통화 PreferredCurrencyHelper = 지원되는 제공업체 비용을 변환하여 표시합니다. 자동은 각 제공업체의 원래 통화를 유지합니다. StartupSettings = 시스템 @@ -17,11 +18,11 @@ StartAtLogin = 로그인 시 자동 실행 StartMinimized = 최소화 상태로 시작 StartAtLoginHelper = 시스템 시작 시 자동으로 앱을 실행합니다 StartMinimizedHelper = 시스템 트레이로 최소화된 상태로 시작합니다 -ShowNotificationsHelper = 사용량 임계값에 도달하면 알림 +ShowNotificationsHelper = 세션 또는 주간 사용량이 아래 임계값을 넘으면 경고합니다. 끄면 할당량 소진 및 복구를 제외한 모든 알림이 꺼집니다. SoundEnabledHelper = 임계값에 도달하면 소리 재생 HighUsageThresholdHelper = 이 사용량 수준에서 경고 표시 CriticalUsageThresholdHelper = 이 수준에서 위험 경고 표시 -ShowNotifications = 알림 표시 +ShowNotifications = 임계값 경고 SoundEnabled = 소리 알림 NotificationSoundTheme = 기본 알림 소리 모음 NotificationSoundThemeHelper = 사용자 지정 WAV 파일이 없는 알림에 사용합니다. @@ -187,6 +188,7 @@ MenuSettings = 설정... MenuAbout = CodexBar 정보 MenuAboutVersion = CodexBar 정보 (v{ "{}" }) MenuQuit = 종료 +QuitCodexBar = CodexBar 종료 TrayMenuSwitchAccount = 계정 전환... TrayMenuUsageDashboard = 사용량 대시보드 TrayMenuStatusPage = 상태 페이지 @@ -349,7 +351,7 @@ Api = API Web = 웹 PrivacyTitle = 개인정보 보호 HidePersonalInfo = 개인 정보 숨기기 -HidePersonalInfoHelper = 이메일 및 계정 이름 마스킹 (스트리밍 시 유용) +HidePersonalInfoHelper = 트레이 메뉴와 패널에서 이메일 주소를 가립니다. 저장된 데이터는 바꾸지 않고 비용 기록의 프로젝트/소스 이름과 경로도 숨깁니다. SectionLocalIntegrations = 로컬 통합 PowerToysPipeLabel = PowerToys 상태 파이프 PowerToysPipeHelper = 명명된 파이프를 통해 캐시된 공급자 상태와 로컬 사용량 합계를 PowerToys Command Palette에 노출합니다. 이메일이나 플랜은 포함되지 않습니다. 재시작이 필요합니다. @@ -420,6 +422,8 @@ AgentSessionsSshHostsHelper = 쉼표로 구분된 SSH 대상입니다. 연결은 AgentSessionsLoading = 에이전트 세션을 찾는 중... AgentSessionsEmpty = 활성 에이전트 세션이 없습니다. UpdatesTitle = 업데이트 +AboutBuilt = 빌드 날짜 { "{}" } +SectionLinks = 링크 UpdateChannelChoice = 업데이트 채널 UpdateChannelChoiceHelper = 안정 버전과 베타 미리보기 버전 중 선택 AutoDownloadUpdates = 업데이트 자동 확인 @@ -436,6 +440,7 @@ ShortcutHintPlaceholder = 예: Ctrl+Shift+U SelectProvider = 제공업체 선택 RefreshInterval30Sec = 30초 RefreshInterval1Min = 1분 +RefreshInterval2Min = 2분 RefreshInterval5Min = 5분 RefreshInterval10Min = 10분 BrowserCookiesTitle = 브라우저 쿠키 @@ -446,7 +451,7 @@ AutoRefreshInterval = 자동 새로고침 간격 AboutDescription = 오리지널 macOS 버전의 Windows 이식 버전입니다. AboutDescriptionLine2 = 시스템 트레이에서 AI 제공업체의 사용량을 추적하세요. ViewOnGitHub = → GitHub에서 보기 -SubmitIssue = → 이슈 제출 +SubmitIssue = 이슈 제출 MaintainedBy = CodexBar 기여자들에 의해 유지 관리됨 CommitLabel = 커밋 BuildDateLabel = 빌드 날짜 @@ -474,7 +479,17 @@ UpdateFailedMessage = 업데이트 실패: { "{}" } UpdateDownloadingMessage = { "{}" } 다운로드 중 ({ "{:.0}" }%) TabTokenAccounts = 토큰 SectionRefresh = 자동화 +SectionRefreshing = 새로 고침 +RefreshManualFooter = 자동 새로 고침이 꺼져 있습니다. 트레이 메뉴의 모두 새로 고침을 사용하세요. SectionNotifications = 알림 +SectionAlerts = 알림 +SessionQuotaNotifications = 할당량 소진 및 복구 +SessionQuotaNotificationsHelper = 제공업체의 세션 할당량이 0% 남았을 때와 다시 사용할 수 있게 되었을 때 알립니다. +WarningAtPercentUsed = 경고 (사용률 %) +CriticalAtPercentUsed = 위험 (사용률 %) +ThresholdsCaption = 제공업체가 재정의하지 않는 한 세션 및 주간 창에 쓰이는 사용률입니다. +ThresholdOverridesTitle = 제공업체별 임계값 +ThresholdOverridesCaption = Codex와 Claude는 자체 경고 및 위험 사용률을 쓸 수 있습니다. 비워 두면 상위 값을 사용합니다. SectionUsageThresholds = 사용량 임계값 SectionKeyboard = 키보드 SectionUsageRendering = 사용량 렌더링 @@ -551,6 +566,9 @@ SectionPreferencesTransfer = 이동 가능한 환경설정 PreferencesTransferCaption = 표시, 새로고침, 알림, 제공업체 설정을 다른 PC로 복사합니다. API 키, 쿠키, 폴더, 호스트는 포함되지 않습니다. PreferencesExportButton = 환경설정 내보내기 PreferencesImportButton = 환경설정 가져오기 +PreferencesTransferFooter = 표시 및 알림 환경설정을 다른 PC에서 가져올 수 있는 파일로 저장합니다. 계정, 자격 증명, 로그인, 로컬 경로 및 동의는 이 PC에 남습니다. +SwitcherShortcutsOpenButton = 제공업체 전환 단축키… +DialogDone = 완료 PreferencesExportSuccess = 환경설정을 내보냈습니다. PreferencesImportSuccess = 환경설정을 가져왔습니다. ShortcutRecordButton = 입력 @@ -617,8 +635,9 @@ CodexLogPathsLabel = Codex 로그 경로 CodexLogPathsHelper = .codex 루트도 사용할 수 있으며, 그 안의 sessions 폴더를 스캔합니다. AvoidKeychainPromptsLabel = 키체인 창 표시 방지 (Claude) AvoidKeychainPromptsHelper = OS 권한 대화 상자가 나타나지 않도록 Claude의 키체인 자격 증명 읽기를 건너뜁니다. -DisableAllKeychainLabel = 모든 키체인 액세스 비활성화 -DisableAllKeychainHelper = 모든 제공업체에 대한 자격 증명/키체인 읽기를 비활성화합니다. 위의 Claude 옵션도 활성화됩니다. +DisableAllKeychainLabel = 자격 증명 관리자 액세스 비활성화 +DisableAllKeychainHelper = 아직 Windows에서 적용되지 않습니다. CodexBar는 계속 자격 증명 관리자, DPAPI 및 브라우저 쿠키를 사용합니다. 이 선택은 차단 기능이 나올 때를 위해 저장됩니다. +CredentialAccessFooter = 자격 증명 관리자 액세스가 비활성화된 동안 Claude 키체인 프롬프트 옵션은 잠겨 있습니다. SectionTheme = 테마 ThemeLabel = 테마 ThemeHelper = 시스템 테마 설정을 자동으로 따릅니다. 라이트 또는 다크 모드는 이 설정을 무시합니다. diff --git a/rust/src/locale/pt-BR.ftl b/rust/src/locale/pt-BR.ftl index 2835706f4a..0ec4fef25e 100644 --- a/rust/src/locale/pt-BR.ftl +++ b/rust/src/locale/pt-BR.ftl @@ -10,6 +10,7 @@ TabAdvanced = Avançado TabAbout = Sobre TabShortcuts = Atalhos InterfaceLanguage = Idioma da interface +GeneralLanguageHelper = Altera o idioma da interface. PreferredCurrencyLabel = Moeda preferida PreferredCurrencyHelper = Converte os custos dos provedores compatíveis para exibição. Automático mantém a moeda de origem de cada provedor. StartupSettings = Sistema @@ -17,11 +18,11 @@ StartAtLogin = Iniciar com o Windows StartMinimized = Iniciar minimizado StartAtLoginHelper = Iniciar o CodexBar automaticamente ao entrar no sistema StartMinimizedHelper = Iniciar minimizado na bandeja do sistema -ShowNotificationsHelper = Alertar quando os limites de uso forem atingidos +ShowNotificationsHelper = Avisa quando o uso de sessão ou semanal ultrapassa os limites abaixo. Desligar silencia todos os alertas, exceto Cota esgotada e restaurada. SoundEnabledHelper = Reproduzir um som quando os limites forem atingidos HighUsageThresholdHelper = Exibir um aviso neste nível de uso CriticalUsageThresholdHelper = Exibir um alerta crítico neste nível -ShowNotifications = Mostrar notificações +ShowNotifications = Avisos de limite SoundEnabled = Alertas sonoros NotificationSoundTheme = Conjunto de sons padrão NotificationSoundThemeHelper = Usado quando nenhum arquivo WAV personalizado está definido. @@ -210,6 +211,7 @@ MenuSettings = Configurações... MenuAbout = Sobre o CodexBar MenuAboutVersion = Sobre o CodexBar (v{ "{}" }) MenuQuit = Sair +QuitCodexBar = Sair do CodexBar TrayMenuSwitchAccount = Trocar de conta... TrayMenuUsageDashboard = Painel de uso TrayMenuStatusPage = Página de status @@ -369,7 +371,7 @@ Api = API Web = Web PrivacyTitle = Privacidade HidePersonalInfo = Ocultar informações pessoais -HidePersonalInfoHelper = Mascarar e-mails e nomes de contas (bom para streaming) +HidePersonalInfoHelper = Oculta endereços de e-mail no menu da bandeja e no painel. Também oculta nomes de projetos/origens e caminhos do histórico de custos sem alterar os dados salvos. SectionLocalIntegrations = Integrações locais PowerToysPipeLabel = Canal de status do PowerToys PowerToysPipeHelper = Exponha o status do provedor em cache e os totais de uso local à paleta de comandos do PowerToys por meio de um canal nomeado. Nenhum e-mail ou plano está incluído. É necessário reiniciar. @@ -436,6 +438,8 @@ AgentSessionsSshHostsHelper = Destinos SSH separados por vírgula. As conexões AgentSessionsLoading = Procurando sessões de agente... AgentSessionsEmpty = Nenhuma sessão de agente ativa. UpdatesTitle = Atualizações +AboutBuilt = Compilado em { "{}" } +SectionLinks = Links UpdateChannelChoice = Canal de atualização UpdateChannelChoiceHelper = Escolha entre versões estáveis e versões beta AutoDownloadUpdates = Verificar atualizações automaticamente @@ -452,6 +456,7 @@ ShortcutHintPlaceholder = por exemplo, Ctrl+Shift+U SelectProvider = Selecione um provedor RefreshInterval30Sec = 30 segundos RefreshInterval1Min = 1 minuto +RefreshInterval2Min = 2 min RefreshInterval5Min = 5 minutos RefreshInterval10Min = 10 minutos BrowserCookiesTitle = Cookies do navegador @@ -462,7 +467,7 @@ AutoRefreshInterval = Intervalo de atualização automática AboutDescription = Uma versão para Windows do aplicativo original para macOS. AboutDescriptionLine2 = Acompanhe o uso de provedores de IA na bandeja do sistema. ViewOnGitHub = → Ver no GitHub -SubmitIssue = → Enviar um problema +SubmitIssue = Enviar um problema MaintainedBy = Mantido por colaboradores do CodexBar CommitLabel = Commit BuildDateLabel = Data da compilação @@ -490,7 +495,17 @@ UpdateFailedMessage = Falha na atualização: { "{}" } UpdateDownloadingMessage = Baixando { "{}" } ({ "{:.0}" }%) TabTokenAccounts = Contas de token SectionRefresh = Automação +SectionRefreshing = Atualização +RefreshManualFooter = A atualização automática está desligada; use Atualizar tudo no menu da bandeja. SectionNotifications = Notificações +SectionAlerts = Alertas +SessionQuotaNotifications = Cota esgotada e restaurada +SessionQuotaNotificationsHelper = Avisa quando a cota de sessão de um provedor chega a 0% restante e quando volta a ficar disponível. +WarningAtPercentUsed = Aviso em % usado +CriticalAtPercentUsed = Crítico em % usado +ThresholdsCaption = Porcentagens usadas para as janelas de sessão e semanais, a menos que um provedor as substitua. +ThresholdOverridesTitle = Limites por provedor +ThresholdOverridesCaption = Codex e Claude podem usar seu próprio % usado de aviso e crítico. Deixe um campo vazio para usar o valor acima. SectionUsageThresholds = Limites de uso SectionKeyboard = Teclado SectionUsageRendering = Renderização de uso @@ -570,8 +585,9 @@ CodexLogPathsLabel = Caminhos de log do Codex CodexLogPathsHelper = Uma raiz .codex é aceita; ele verificará a pasta de sessões dentro dela. AvoidKeychainPromptsLabel = Evite avisos de chaveiro (Claude) AvoidKeychainPromptsHelper = Ignore as leituras de credenciais das chaves de Claude para evitar caixas de diálogo de permissão do sistema operacional. -DisableAllKeychainLabel = Desative todo o acesso às chaves -DisableAllKeychainHelper = Desative as leituras de credenciais/chaves para todos os provedores. Também habilita a opção Claude acima. +DisableAllKeychainLabel = Desativar o acesso ao Gerenciador de Credenciais +DisableAllKeychainHelper = Ainda não é aplicado no Windows: o CodexBar continua usando o Gerenciador de Credenciais, a DPAPI e os cookies do navegador. A escolha fica salva para quando o bloqueio chegar. +CredentialAccessFooter = Enquanto o acesso ao Gerenciador de Credenciais estiver desativado, a opção de avisos de chaves do Claude fica bloqueada. SectionTheme = Aparência ThemeLabel = Tema ThemeHelper = Auto segue o esquema de cores do seu sistema. Claro e Escuro o substituem. @@ -984,6 +1000,9 @@ SectionPreferencesTransfer = Preferências portáteis PreferencesTransferCaption = Copie as escolhas de exibição, atualização, notificações e provedores para outro PC. Chaves de API, cookies, pastas e hosts nunca são incluídos. PreferencesExportButton = Exportar preferências PreferencesImportButton = Importar preferências +PreferencesTransferFooter = Salve as preferências de exibição e notificação em um arquivo que pode ser importado em outro PC. Contas, credenciais, login, caminhos locais e consentimentos ficam neste PC. +SwitcherShortcutsOpenButton = Atalhos do seletor de provedores… +DialogDone = Concluído PreferencesExportSuccess = Preferências exportadas. PreferencesImportSuccess = Preferências importadas. SwitcherShortcutsTitle = Atalhos do seletor de provedores diff --git a/rust/src/locale/ru-RU.ftl b/rust/src/locale/ru-RU.ftl index 7446881d6f..a1f4ba55cb 100644 --- a/rust/src/locale/ru-RU.ftl +++ b/rust/src/locale/ru-RU.ftl @@ -10,6 +10,7 @@ TabAdvanced = Дополнительно TabAbout = О программе TabShortcuts = Горячие клавиши InterfaceLanguage = Язык интерфейса +GeneralLanguageHelper = Изменить язык интерфейса. PreferredCurrencyLabel = Валюта PreferredCurrencyHelper = Пересчитывать расходы поддерживаемых провайдеров для отображения. «Авто» оставляет исходную валюту каждого провайдера. StartupSettings = Система @@ -17,11 +18,11 @@ StartAtLogin = Запускать при входе в систему StartMinimized = Запускать свернутым StartAtLoginHelper = Автоматически запускать после входа в систему StartMinimizedHelper = Запускать свернутым в системный трей -ShowNotificationsHelper = Оповещение при достижении порогов использования +ShowNotificationsHelper = Предупреждает, когда сессионное или недельное использование превышает пороги ниже. Если выключить, замолкнут все оповещения, кроме «Квота исчерпана и восстановлена». SoundEnabledHelper = Воспроизведение звука при достижении пороговых значений HighUsageThresholdHelper = Показывать предупреждение на этом уровне использования CriticalUsageThresholdHelper = Показывать критическое оповещение на этом уровне -ShowNotifications = Показать уведомления +ShowNotifications = Предупреждения о порогах SoundEnabled = Звуковые оповещения NotificationSoundTheme = Набор звуков по умолчанию NotificationSoundThemeHelper = Используется, если свой WAV-файл не назначен. @@ -214,6 +215,7 @@ MenuSettings = Настройки... MenuAbout = О CodexBar MenuAboutVersion = О CodexBar (v{ "{}" }) MenuQuit = Выйти +QuitCodexBar = Выйти из CodexBar TrayMenuSwitchAccount = Сменить аккаунт... TrayMenuUsageDashboard = Панель управления использованием TrayMenuStatusPage = Страница статуса @@ -376,7 +378,7 @@ Api = API Web = Интернет PrivacyTitle = Конфиденциальность HidePersonalInfo = Скрыть личную информацию -HidePersonalInfoHelper = Маскируйте адреса электронной почты и имена учетных записей (хорошо для потоковой передачи) +HidePersonalInfoHelper = Скрывает адреса электронной почты в меню трея и на панели. Также скрывает названия проектов и источников и пути в истории затрат, не меняя сохранённые данные. SectionLocalIntegrations = Локальная интеграция PowerToysPipeLabel = Строка состояния PowerToys PowerToysPipeHelper = Предоставляйте статус кэшированного провайдера и общие сведения о локальном использовании палитре команд PowerToys через именованный канал. Никакие электронные письма или планы не включены. Требуется перезагрузка. @@ -447,6 +449,8 @@ AgentSessionsSshHostsHelper = Цели SSH, разделенные запяты AgentSessionsLoading = Ищу агентские сессии... AgentSessionsEmpty = Нет сеансов активных агентов. UpdatesTitle = Обновления +AboutBuilt = Сборка от { "{}" } +SectionLinks = Ссылки UpdateChannelChoice = Обновить канал UpdateChannelChoiceHelper = Выбирайте между стабильной и бета-версией предварительного просмотра. AutoDownloadUpdates = Автоматическая проверка обновлений @@ -463,6 +467,7 @@ ShortcutHintPlaceholder = например, Ctrl+Shift+U SelectProvider = Выберите провайдера RefreshInterval30Sec = 30 секунд RefreshInterval1Min = 1 мин. +RefreshInterval2Min = 2 мин RefreshInterval5Min = 5 минут RefreshInterval10Min = 10 минут BrowserCookiesTitle = Куки браузера @@ -473,7 +478,7 @@ AutoRefreshInterval = Интервал автоматического обнов AboutDescription = Порт Windows исходной версии macOS. AboutDescriptionLine2 = Отслеживайте использование провайдера ИИ на панели задач. ViewOnGitHub = → Посмотреть на GitHub -SubmitIssue = → Отправить вопрос +SubmitIssue = Отправить вопрос MaintainedBy = Поддерживается участниками CodexBar CommitLabel = Зафиксировать BuildDateLabel = Построен @@ -501,7 +506,17 @@ UpdateFailedMessage = Не удалось обновить: { "{}" }. UpdateDownloadingMessage = Загрузка { "{}" } ({ "{:.0}" }%) TabTokenAccounts = Токены SectionRefresh = Автоматизация +SectionRefreshing = Обновление +RefreshManualFooter = Автообновление выключено; используйте «Обновить всё» в меню трея. SectionNotifications = Уведомления +SectionAlerts = Оповещения +SessionQuotaNotifications = Квота исчерпана и восстановлена +SessionQuotaNotificationsHelper = Сообщает, когда сессионная квота провайдера опускается до 0 % и когда она снова становится доступной. +WarningAtPercentUsed = Предупреждение при % использования +CriticalAtPercentUsed = Критично при % использования +ThresholdsCaption = Процент использования для сессионных и недельных окон, если провайдер не задаёт свои значения. +ThresholdOverridesTitle = Пороги провайдеров +ThresholdOverridesCaption = Codex и Claude могут использовать свои пороги предупреждения и критичности. Оставьте поле пустым, чтобы взять значение уровнем выше. SectionUsageThresholds = Пороги использования SectionKeyboard = Клавиатура SectionUsageRendering = Рендеринг использования @@ -533,6 +548,9 @@ SectionPreferencesTransfer = Перенос настроек PreferencesTransferCaption = Перенести на другой компьютер настройки отображения, обновления, уведомлений и выбор провайдеров. API-ключи, куки, папки и адреса хостов не переносятся. PreferencesExportButton = Экспортировать настройки PreferencesImportButton = Импортировать настройки +PreferencesTransferFooter = Сохраните настройки отображения и уведомлений в файл, который можно импортировать на другом ПК. Учётные записи, учётные данные, автозапуск, локальные пути и согласия остаются на этом ПК. +SwitcherShortcutsOpenButton = Сочетания клавиш для переключения провайдеров… +DialogDone = Готово PreferencesExportSuccess = Настройки экспортированы. PreferencesImportSuccess = Настройки импортированы. ShortcutRecordButton = Запись @@ -599,8 +617,9 @@ CodexLogPathsLabel = Пути журналов Codex CodexLogPathsHelper = Принимается корень .codex; он просканирует папку сеансов внутри него. AvoidKeychainPromptsLabel = Избегайте подсказок хранилища учетных данных (Claude) AvoidKeychainPromptsHelper = Пропустить чтение учетных данных хранилища учетных данных для Claude, чтобы предотвратить диалоговые окна разрешений ОС. -DisableAllKeychainLabel = Отключить весь доступ к связке ключей -DisableAllKeychainHelper = Отключите чтение учетных данных/хранилища учетных данных для всех провайдеров. Также включает опцию Claude, указанную выше. +DisableAllKeychainLabel = Отключить доступ к диспетчеру учётных данных +DisableAllKeychainHelper = Пока не применяется в Windows: CodexBar по-прежнему использует диспетчер учётных данных, DPAPI и файлы cookie браузера. Выбор сохраняется до появления блокировки. +CredentialAccessFooter = Пока доступ к диспетчеру учётных данных отключён, параметр запросов связки ключей для Claude заблокирован. SectionTheme = Внешний вид ThemeLabel = Тема ThemeHelper = Автоматически соответствует цветовой схеме вашей системы. Свет и Тьма отвергают это. diff --git a/rust/src/locale/tests.rs b/rust/src/locale/tests.rs index 9e9443e30e..477d86b21f 100644 --- a/rust/src/locale/tests.rs +++ b/rust/src/locale/tests.rs @@ -9,11 +9,11 @@ fn test_locale_key_english() { ); assert_eq!( get_text(Language::English, LocaleKey::InterfaceLanguage), - "Interface Language" + "Language" ); assert_eq!( get_text(Language::English, LocaleKey::StartAtLogin), - "Start at Login" + "Start at login" ); } diff --git a/rust/src/locale/tr-TR.ftl b/rust/src/locale/tr-TR.ftl index bd2ed50761..c85258c0c4 100644 --- a/rust/src/locale/tr-TR.ftl +++ b/rust/src/locale/tr-TR.ftl @@ -10,6 +10,7 @@ TabAdvanced = Gelişmiş TabAbout = Hakkında TabShortcuts = Kısayollar InterfaceLanguage = Arayüz Dili +GeneralLanguageHelper = Arayüz dilini değiştirir. PreferredCurrencyLabel = Tercih edilen para birimi PreferredCurrencyHelper = Desteklenen sağlayıcı maliyetlerini görüntüleme için dönüştürür. Otomatik, her sağlayıcının kaynak para birimini korur. StartupSettings = Sistem @@ -17,11 +18,11 @@ StartAtLogin = Girişte başlat StartMinimized = Simge Durumunda Başlat StartAtLoginHelper = Sistem açıldıktan sonra otomatik olarak başlat StartMinimizedHelper = Sistem tepsisinde simge durumunda başlat -ShowNotificationsHelper = Kullanım eşiklerine ulaşıldığında uyar +ShowNotificationsHelper = Oturum veya haftalık kullanım aşağıdaki eşikleri geçtiğinde uyarır. Kapatılırsa Kota tükendi ve yenilendi dışındaki tüm uyarılar susar. SoundEnabledHelper = Eşiklere ulaşıldığında ses çal HighUsageThresholdHelper = Bu kullanım düzeyinde uyarı göster CriticalUsageThresholdHelper = Bu düzeyde kritik uyarı göster -ShowNotifications = Bildirimleri Göster +ShowNotifications = Eşik uyarıları SoundEnabled = Sesli Uyarılar NotificationSoundTheme = Varsayılan ses grubu NotificationSoundThemeHelper = Özel bir WAV dosyası atanmadığında kullanılır. @@ -214,6 +215,7 @@ MenuSettings = Ayarlar... MenuAbout = CodexBar hakkında MenuAboutVersion = CodexBar hakkında (v{ "{}" }) MenuQuit = Çık +QuitCodexBar = CodexBar'dan Çık TrayMenuSwitchAccount = Hesap değiştir... TrayMenuUsageDashboard = Kullanım panosu TrayMenuStatusPage = Durum sayfası @@ -376,7 +378,7 @@ Api = API Web = Web PrivacyTitle = Gizlilik HidePersonalInfo = Kişisel Bilgileri Gizle -HidePersonalInfoHelper = E-postaları ve hesap adlarını maskele (yayın için uygundur) +HidePersonalInfoHelper = Tepsi menüsünde ve panelde e-posta adreslerini gizler. Kayıtlı verileri değiştirmeden maliyet geçmişindeki proje/kaynak adlarını ve yolları da gizler. SectionLocalIntegrations = Yerel entegrasyonlar PowerToysPipeLabel = PowerToys durum kanalı PowerToysPipeHelper = Önbelleğe alınmış sağlayıcı durumunu ve yerel kullanım toplamlarını adlandırılmış kanal aracılığıyla PowerToys Komut Paleti'ne sunar. E-posta veya plan bilgisi dahil edilmez. Yeniden başlatma gerekir. @@ -447,6 +449,8 @@ AgentSessionsSshHostsHelper = Virgülle ayrılmış SSH hedefleri. Bağlantılar AgentSessionsLoading = Aracı oturumları aranıyor... AgentSessionsEmpty = Etkin aracı oturumu yok. UpdatesTitle = Güncellemeler +AboutBuilt = Derleme tarihi { "{}" } +SectionLinks = Bağlantılar UpdateChannelChoice = Güncelleme kanalı UpdateChannelChoiceHelper = Kararlı ve beta önizleme sürümleri arasında seçim yapın AutoDownloadUpdates = Güncellemeleri otomatik denetle @@ -463,6 +467,7 @@ ShortcutHintPlaceholder = örn. Ctrl+Shift+U SelectProvider = Bir sağlayıcı seçin RefreshInterval30Sec = 30 sn RefreshInterval1Min = 1 dk +RefreshInterval2Min = 2 dk RefreshInterval5Min = 5 dk RefreshInterval10Min = 10 dk BrowserCookiesTitle = Tarayıcı Çerezleri @@ -473,7 +478,7 @@ AutoRefreshInterval = Otomatik yenileme aralığı AboutDescription = Orijinal macOS sürümünün Windows uyarlaması. AboutDescriptionLine2 = Yapay zekâ sağlayıcı kullanımını sistem tepsisinden izleyin. ViewOnGitHub = → GitHub'da Görüntüle -SubmitIssue = → Sorun Bildir +SubmitIssue = Sorun Bildir MaintainedBy = CodexBar katkıda bulunanları tarafından sürdürülüyor CommitLabel = Kayıt BuildDateLabel = Derleme @@ -501,7 +506,17 @@ UpdateFailedMessage = Güncelleme başarısız: { "{}" } UpdateDownloadingMessage = { "{}" } indiriliyor (%{ "{:.0}" }) TabTokenAccounts = Tokenlar SectionRefresh = Otomasyon +SectionRefreshing = Yenileme +RefreshManualFooter = Otomatik yenileme kapalı; tepsi menüsündeki Tümünü Yenile komutunu kullanın. SectionNotifications = Bildirimler +SectionAlerts = Uyarılar +SessionQuotaNotifications = Kota tükendi ve yenilendi +SessionQuotaNotificationsHelper = Bir sağlayıcının oturum kotası %0 kaldığında ve yeniden kullanılabilir olduğunda bildirir. +WarningAtPercentUsed = Uyarı (% kullanım) +CriticalAtPercentUsed = Kritik (% kullanım) +ThresholdsCaption = Bir sağlayıcı geçersiz kılmadıkça oturum ve haftalık pencereler için kullanım yüzdeleri. +ThresholdOverridesTitle = Sağlayıcı eşikleri +ThresholdOverridesCaption = Codex ve Claude kendi uyarı ve kritik kullanım yüzdelerini kullanabilir. Üstteki değeri kullanmak için alanı boş bırakın. SectionUsageThresholds = Kullanım Eşikleri SectionKeyboard = Klavye SectionUsageRendering = Kullanım gösterimi @@ -533,6 +548,9 @@ SectionPreferencesTransfer = Taşınabilir tercihler PreferencesTransferCaption = Görüntüleme, yenileme, bildirim ve sağlayıcı seçimlerini başka bir bilgisayara kopyalayın. API anahtarları, çerezler, klasörler ve sunucular asla dahil edilmez. PreferencesExportButton = Tercihleri dışa aktar PreferencesImportButton = Tercihleri içe aktar +PreferencesTransferFooter = Görünüm ve bildirim tercihlerini başka bir bilgisayarda içe aktarabileceğiniz bir dosyaya kaydedin. Hesaplar, kimlik bilgileri, oturum açma, yerel yollar ve onaylar bu bilgisayarda kalır. +SwitcherShortcutsOpenButton = Sağlayıcı Değiştirici Kısayolları… +DialogDone = Bitti PreferencesExportSuccess = Tercihler dışa aktarıldı. PreferencesImportSuccess = Tercihler içe aktarıldı. ShortcutRecordButton = Kaydet @@ -599,8 +617,9 @@ CodexLogPathsLabel = Codex günlük yolları CodexLogPathsHelper = Bir .codex kökü kabul edilir; içindeki sessions klasörü taranır. AvoidKeychainPromptsLabel = Anahtar zinciri istemlerinden kaçın (Claude) AvoidKeychainPromptsHelper = İşletim sistemi izin iletişim kutularını önlemek için Claude anahtar zinciri kimlik bilgisi okumalarını atla. -DisableAllKeychainLabel = Tüm anahtar zinciri erişimini devre dışı bırak -DisableAllKeychainHelper = Tüm sağlayıcılar için kimlik bilgisi/anahtar zinciri okumalarını kapat. Yukarıdaki Claude seçeneğini de etkinleştirir. +DisableAllKeychainLabel = Kimlik Bilgileri Yöneticisi erişimini devre dışı bırak +DisableAllKeychainHelper = Windows'ta henüz uygulanmıyor: CodexBar Kimlik Bilgileri Yöneticisi'ni, DPAPI'yi ve tarayıcı çerezlerini kullanmaya devam eder. Seçim, engelleme geldiğinde kullanılmak üzere kaydedilir. +CredentialAccessFooter = Kimlik Bilgileri Yöneticisi erişimi kapalıyken Claude anahtar zinciri istemi seçeneği kilitli kalır. SectionTheme = Görünüm ThemeLabel = Tema ThemeHelper = Otomatik, sisteminizin renk düzenini izler. Açık ve Koyu bunu geçersiz kılar. diff --git a/rust/src/locale/uk-UA.ftl b/rust/src/locale/uk-UA.ftl index cac3dabe7d..904c6f6f2d 100644 --- a/rust/src/locale/uk-UA.ftl +++ b/rust/src/locale/uk-UA.ftl @@ -10,6 +10,7 @@ TabAdvanced = Додатково TabAbout = Про програму TabShortcuts = Комбінації клавіш InterfaceLanguage = Мова інтерфейсу +GeneralLanguageHelper = Змінити мову інтерфейсу. PreferredCurrencyLabel = Валюта PreferredCurrencyHelper = Перераховувати витрати підтримуваних провайдерів для показу. «Авто» зберігає власну валюту кожного провайдера. StartupSettings = Система @@ -17,11 +18,11 @@ StartAtLogin = Запускати під час входу в систему StartMinimized = Запускати згорнутою StartAtLoginHelper = Автоматично запускати після входу в систему StartMinimizedHelper = Запускати згорнутою в системний трей -ShowNotificationsHelper = Сповіщати, коли досягнуто порогів використання +ShowNotificationsHelper = Попереджає, коли сесійне або тижневе використання перетинає пороги нижче. Якщо вимкнути, замовкнуть усі сповіщення, крім «Квоту вичерпано й відновлено». SoundEnabledHelper = Відтворювати звук, коли досягнуто порогів HighUsageThresholdHelper = Показувати попередження на цьому рівні використання CriticalUsageThresholdHelper = Показувати критичне сповіщення на цьому рівні -ShowNotifications = Показувати сповіщення +ShowNotifications = Попередження про пороги SoundEnabled = Звукові сповіщення NotificationSoundTheme = Типовий набір звуків NotificationSoundThemeHelper = Використовується, якщо власний WAV-файл не призначено. @@ -214,6 +215,7 @@ MenuSettings = Налаштування... MenuAbout = Про CodexBar MenuAboutVersion = Про CodexBar (v{ "{}" }) MenuQuit = Вийти +QuitCodexBar = Вийти з CodexBar TrayMenuSwitchAccount = Перемкнути обліковий запис... TrayMenuUsageDashboard = Панель використання TrayMenuStatusPage = Сторінка стану @@ -376,7 +378,7 @@ Api = API Web = Веб PrivacyTitle = Конфіденційність HidePersonalInfo = Приховати особисті дані -HidePersonalInfoHelper = Маскувати адреси пошти й імена облікових записів (зручно для трансляцій) +HidePersonalInfoHelper = Приховує адреси електронної пошти в меню трею та на панелі. Також приховує назви проєктів і джерел та шляхи в історії витрат, не змінюючи збережені дані. SectionLocalIntegrations = Локальні інтеграції PowerToysPipeLabel = Канал стану для PowerToys PowerToysPipeHelper = Передавати кешований стан провайдерів і підсумки локального використання в Command Palette PowerToys через іменований канал. Адреси пошти й плани не передаються. Потрібен перезапуск. @@ -447,6 +449,8 @@ AgentSessionsSshHostsHelper = SSH-адреси через кому. З’єдн AgentSessionsLoading = Пошук сеансів агентів... AgentSessionsEmpty = Немає активних сеансів агентів. UpdatesTitle = Оновлення +AboutBuilt = Збірка від { "{}" } +SectionLinks = Посилання UpdateChannelChoice = Канал оновлень UpdateChannelChoiceHelper = Виберіть стабільні версії або попередні бета-версії AutoDownloadUpdates = Автоматично перевіряти оновлення @@ -463,6 +467,7 @@ ShortcutHintPlaceholder = напр., Ctrl+Shift+U SelectProvider = Виберіть провайдера RefreshInterval30Sec = 30 с RefreshInterval1Min = 1 хв +RefreshInterval2Min = 2 хв RefreshInterval5Min = 5 хв RefreshInterval10Min = 10 хв BrowserCookiesTitle = Файли cookie браузера @@ -473,7 +478,7 @@ AutoRefreshInterval = Інтервал автооновлення AboutDescription = Порт для Windows оригінальної версії для macOS. AboutDescriptionLine2 = Стежте за використанням AI-провайдерів у системному треї. ViewOnGitHub = → Переглянути на GitHub -SubmitIssue = → Повідомити про проблему +SubmitIssue = Повідомити про проблему MaintainedBy = Підтримують учасники проєкту CodexBar CommitLabel = Коміт BuildDateLabel = Зібрано @@ -501,7 +506,17 @@ UpdateFailedMessage = Не вдалося оновити: { "{}" } UpdateDownloadingMessage = Завантаження { "{}" } ({ "{:.0}" }%) TabTokenAccounts = Токени SectionRefresh = Автоматизація +SectionRefreshing = Оновлення +RefreshManualFooter = Автооновлення вимкнено; скористайтеся «Оновити все» в меню трею. SectionNotifications = Сповіщення +SectionAlerts = Сповіщення +SessionQuotaNotifications = Квоту вичерпано й відновлено +SessionQuotaNotificationsHelper = Повідомляє, коли сесійна квота провайдера падає до 0 % і коли вона знову стає доступною. +WarningAtPercentUsed = Попередження при % використання +CriticalAtPercentUsed = Критично при % використання +ThresholdsCaption = Відсотки використання для сесійних і тижневих вікон, якщо провайдер не задає власні. +ThresholdOverridesTitle = Пороги провайдерів +ThresholdOverridesCaption = Codex і Claude можуть мати власні пороги попередження та критичності. Залиште поле порожнім, щоб узяти значення рівнем вище. SectionUsageThresholds = Пороги використання SectionKeyboard = Клавіатура SectionUsageRendering = Відображення використання @@ -533,6 +548,9 @@ SectionPreferencesTransfer = Перенесення налаштувань PreferencesTransferCaption = Перенесіть на інший комп’ютер налаштування відображення, оновлення, сповіщень і вибір провайдерів. API-ключі, файли cookie, папки й хости не переносяться ніколи. PreferencesExportButton = Експортувати налаштування PreferencesImportButton = Імпортувати налаштування +PreferencesTransferFooter = Збережіть налаштування відображення та сповіщень у файл, який можна імпортувати на іншому ПК. Облікові записи, облікові дані, автозапуск, локальні шляхи та згоди залишаються на цьому ПК. +SwitcherShortcutsOpenButton = Комбінації клавіш для перемикання провайдерів… +DialogDone = Готово PreferencesExportSuccess = Налаштування експортовано. PreferencesImportSuccess = Налаштування імпортовано. ShortcutRecordButton = Записати @@ -599,8 +617,9 @@ CodexLogPathsLabel = Шляхи до журналів Codex CodexLogPathsHelper = Можна вказати корінь .codex – тоді буде проскановано папку sessions усередині нього. AvoidKeychainPromptsLabel = Без запитів сховища облікових даних (Claude) AvoidKeychainPromptsHelper = Не читати облікові дані Claude зі сховища, щоб ОС не показувала запити дозволу. -DisableAllKeychainLabel = Вимкнути доступ до сховища облікових даних -DisableAllKeychainHelper = Не читати облікові дані зі сховища для всіх провайдерів. Також вмикає параметр для Claude вище. +DisableAllKeychainLabel = Вимкнути доступ до диспетчера облікових даних +DisableAllKeychainHelper = Поки не застосовується в Windows: CodexBar і далі використовує диспетчер облікових даних, DPAPI та файли cookie браузера. Вибір зберігається до появи блокування. +CredentialAccessFooter = Поки доступ до диспетчера облікових даних вимкнено, параметр запитів сховища ключів для Claude заблоковано. SectionTheme = Вигляд ThemeLabel = Тема ThemeHelper = «Авто» стежить за колірною схемою системи. «Світла» й «Темна» її перевизначають. diff --git a/rust/src/locale/zh-CN.ftl b/rust/src/locale/zh-CN.ftl index b60e376fbb..2be0c75380 100644 --- a/rust/src/locale/zh-CN.ftl +++ b/rust/src/locale/zh-CN.ftl @@ -10,6 +10,7 @@ TabAdvanced = 高级 TabAbout = 关于 TabShortcuts = 快捷键 InterfaceLanguage = 界面语言 +GeneralLanguageHelper = 更改界面显示语言。 PreferredCurrencyLabel = 首选货币 PreferredCurrencyHelper = 转换受支持服务商的费用后显示。“自动”保留各服务商的原始货币。 StartupSettings = 系统 @@ -17,11 +18,11 @@ StartAtLogin = 开机启动 StartMinimized = 最小化启动 StartAtLoginHelper = 登录后自动启动 CodexBar StartMinimizedHelper = 启动后停留在系统托盘 -ShowNotificationsHelper = 达到用量阈值时提醒 +ShowNotificationsHelper = 会话或每周用量超过下方阈值时警告。关闭后,除“额度耗尽与恢复”外的所有提醒都会静音。 SoundEnabledHelper = 达到阈值时播放提示音 HighUsageThresholdHelper = 在该用量水平显示预警 CriticalUsageThresholdHelper = 在该水平显示严重告警 -ShowNotifications = 显示通知 +ShowNotifications = 阈值警告 SoundEnabled = 声音提示 NotificationSoundTheme = 默认通知声音组 NotificationSoundThemeHelper = 用于未指定自定义 WAV 文件的通知。 @@ -187,6 +188,7 @@ MenuSettings = 设置... MenuAbout = 关于 CodexBar MenuAboutVersion = 关于 CodexBar (v{ "{}" }) MenuQuit = 退出 +QuitCodexBar = 退出 CodexBar TrayMenuSwitchAccount = 切换账号... TrayMenuUsageDashboard = 用量仪表盘 TrayMenuStatusPage = 状态页面 @@ -349,7 +351,7 @@ Api = API Web = 网页 PrivacyTitle = 隐私 HidePersonalInfo = 隐藏个人信息 -HidePersonalInfoHelper = 遮蔽邮箱和账号名称(适合直播时使用) +HidePersonalInfoHelper = 在托盘菜单和面板中隐藏邮箱地址。同时隐藏费用历史中的项目/来源名称和路径,不改变已存储的数据。 SectionLocalIntegrations = 本机集成 PowerToysPipeLabel = PowerToys 状态管道 PowerToysPipeHelper = 通过命名管道向 PowerToys Command Palette 暴露已缓存的提供商状态和本地用量汇总。不包含邮箱或套餐。需要重启后生效。 @@ -420,6 +422,8 @@ AgentSessionsSshHostsHelper = 以逗号分隔的 SSH 目标。连接使用非交 AgentSessionsLoading = 正在查找 Agent 会话… AgentSessionsEmpty = 没有活跃的 Agent 会话。 UpdatesTitle = 更新 +AboutBuilt = 构建于 { "{}" } +SectionLinks = 链接 UpdateChannelChoice = 更新通道 UpdateChannelChoiceHelper = 在稳定版与测试预览版之间选择 AutoDownloadUpdates = 自动检查更新 @@ -436,6 +440,7 @@ ShortcutHintPlaceholder = 例如:Ctrl+Shift+U SelectProvider = 请选择服务商 RefreshInterval30Sec = 30 秒 RefreshInterval1Min = 1 分钟 +RefreshInterval2Min = 2 分钟 RefreshInterval5Min = 5 分钟 RefreshInterval10Min = 10 分钟 BrowserCookiesTitle = 浏览器 Cookie @@ -446,7 +451,7 @@ AutoRefreshInterval = 自动刷新间隔 AboutDescription = CodexBar 的 Windows 移植版本。 AboutDescriptionLine2 = 在系统托盘中追踪 AI 服务商用量。 ViewOnGitHub = → 查看 GitHub -SubmitIssue = → 提交问题 +SubmitIssue = 提交问题 MaintainedBy = 由 CodexBar 贡献者维护 CommitLabel = 提交 BuildDateLabel = 构建 @@ -474,7 +479,17 @@ UpdateFailedMessage = 更新失败:{ "{}" } UpdateDownloadingMessage = 正在下载 { "{}" } ({ "{:.0}" }%) TabTokenAccounts = 令牌 SectionRefresh = 自动化 +SectionRefreshing = 刷新 +RefreshManualFooter = 自动刷新已关闭;请使用托盘菜单中的“全部刷新”。 SectionNotifications = 通知 +SectionAlerts = 提醒 +SessionQuotaNotifications = 额度耗尽与恢复 +SessionQuotaNotificationsHelper = 当服务商的会话额度剩余 0% 以及重新可用时通知。 +WarningAtPercentUsed = 警告(已用 %) +CriticalAtPercentUsed = 严重(已用 %) +ThresholdsCaption = 会话和每周窗口使用的已用百分比,服务商可单独覆盖。 +ThresholdOverridesTitle = 服务商阈值 +ThresholdOverridesCaption = Codex 和 Claude 可以使用自己的警告和严重已用百分比。留空则使用上一级的值。 SectionUsageThresholds = 用量阈值 SectionKeyboard = 键盘 SectionUsageRendering = 用量展示 @@ -506,6 +521,9 @@ SectionPreferencesTransfer = 可移植偏好设置 PreferencesTransferCaption = 将显示、刷新、通知和服务商选项复制到另一台电脑。不包含 API 密钥、Cookie、文件夹和主机。 PreferencesExportButton = 导出偏好设置 PreferencesImportButton = 导入偏好设置 +PreferencesTransferFooter = 将显示和通知偏好保存为文件,可在另一台电脑上导入。账号、凭据、登录启动、本地路径和授权同意保留在这台电脑上。 +SwitcherShortcutsOpenButton = 服务商切换快捷键… +DialogDone = 完成 PreferencesExportSuccess = 偏好设置已导出。 PreferencesImportSuccess = 偏好设置已导入。 ShortcutRecordButton = 录制 @@ -572,8 +590,9 @@ CodexLogPathsLabel = Codex 日志路径 CodexLogPathsHelper = 可以填写 .codex 根目录;应用会扫描其中的 sessions 文件夹。 AvoidKeychainPromptsLabel = 避免钥匙串弹窗(Claude) AvoidKeychainPromptsHelper = 跳过 Claude 的钥匙串凭据读取,避免系统权限弹窗。 -DisableAllKeychainLabel = 禁用所有钥匙串访问 -DisableAllKeychainHelper = 关闭所有服务商的凭据/钥匙串读取。同时启用上方的 Claude 选项。 +DisableAllKeychainLabel = 禁用凭据管理器访问 +DisableAllKeychainHelper = Windows 上尚未生效:CodexBar 仍会使用凭据管理器、DPAPI 和浏览器 Cookie。此选项会保存,待拦截功能上线后使用。 +CredentialAccessFooter = 停用凭据管理器访问期间,Claude 钥匙串提示选项保持锁定。 SectionTheme = 外观 ThemeLabel = 主题 ThemeHelper = 自动跟随系统配色方案;浅色/深色可手动覆盖。 diff --git a/rust/src/locale/zh-TW.ftl b/rust/src/locale/zh-TW.ftl index 8ffe255101..7ca82e57f4 100644 --- a/rust/src/locale/zh-TW.ftl +++ b/rust/src/locale/zh-TW.ftl @@ -10,6 +10,7 @@ TabAdvanced = 高階 TabAbout = 關於 TabShortcuts = 快捷鍵 InterfaceLanguage = 介面語言 +GeneralLanguageHelper = 變更介面顯示語言。 PreferredCurrencyLabel = 偏好貨幣 PreferredCurrencyHelper = 轉換支援的提供者費用以供顯示。自動會保留各提供者的原始貨幣。 StartupSettings = 系統 @@ -17,11 +18,11 @@ StartAtLogin = 登入時啟動 StartMinimized = 最小化啟動 StartAtLoginHelper = 登入後自動啟動 CodexBar StartMinimizedHelper = 啟動後停留在系統系統匣 -ShowNotificationsHelper = 達到用量臨界值時提醒 +ShowNotificationsHelper = 工作階段或每週用量超過下方臨界值時警告。關閉後,除「額度耗盡與恢復」外的所有提醒都會靜音。 SoundEnabledHelper = 達到臨界值時播放提示音 HighUsageThresholdHelper = 在該用量水平顯示預警 CriticalUsageThresholdHelper = 在該水平顯示嚴重告警 -ShowNotifications = 顯示通知 +ShowNotifications = 臨界值警告 SoundEnabled = 聲音提示 NotificationSoundTheme = 預設通知聲音組 NotificationSoundThemeHelper = 用於未指定自訂 WAV 檔案的通知。 @@ -187,6 +188,7 @@ MenuSettings = 設定... MenuAbout = 關於 CodexBar MenuAboutVersion = 關於 CodexBar (v{ "{}" }) MenuQuit = 退出 +QuitCodexBar = 結束 CodexBar TrayMenuSwitchAccount = 切換賬號... TrayMenuUsageDashboard = 用量儀表板 TrayMenuStatusPage = 狀態頁面 @@ -349,7 +351,7 @@ Api = API Web = 網頁 PrivacyTitle = 隱私 HidePersonalInfo = 隱藏個人資訊 -HidePersonalInfoHelper = 遮蔽郵箱和賬號名稱(適合直播時使用) +HidePersonalInfoHelper = 在系統匣選單和面板中隱藏電子郵件地址。同時隱藏費用歷史中的專案/來源名稱和路徑,不改變已儲存的資料。 SectionLocalIntegrations = 本機整合 PowerToysPipeLabel = PowerToys 狀態管道 PowerToysPipeHelper = 透過具名管道向 PowerToys Command Palette 公開已快取的供應商狀態與本機使用量總計。不包含電子郵件或方案資訊。需要重新啟動。 @@ -420,6 +422,8 @@ AgentSessionsSshHostsHelper = 以逗號分隔的 SSH 目標。連線使用非互 AgentSessionsLoading = 正在查找 Agent 工作階段… AgentSessionsEmpty = 沒有活躍的 Agent 工作階段。 UpdatesTitle = 更新 +AboutBuilt = 建置於 { "{}" } +SectionLinks = 連結 UpdateChannelChoice = 更新通道 UpdateChannelChoiceHelper = 在穩定版與測試預覽版之間選擇 AutoDownloadUpdates = 自動檢查更新 @@ -436,6 +440,7 @@ ShortcutHintPlaceholder = 例如:Ctrl+Shift+U SelectProvider = 請選擇提供者 RefreshInterval30Sec = 30 秒 RefreshInterval1Min = 1 分鐘 +RefreshInterval2Min = 2 分鐘 RefreshInterval5Min = 5 分鐘 RefreshInterval10Min = 10 分鐘 BrowserCookiesTitle = 瀏覽器 Cookie @@ -446,7 +451,7 @@ AutoRefreshInterval = 自動重新整理間隔 AboutDescription = CodexBar 的 Windows 移植版本。 AboutDescriptionLine2 = 在系統系統匣中追蹤 AI 提供者用量。 ViewOnGitHub = → 檢視 GitHub -SubmitIssue = → 提交問題 +SubmitIssue = 提交問題 MaintainedBy = 由 CodexBar 貢獻者維護 CommitLabel = 提交 BuildDateLabel = 構建 @@ -474,7 +479,17 @@ UpdateFailedMessage = 更新失敗:{ "{}" } UpdateDownloadingMessage = 正在下載 { "{}" } ({ "{:.0}" }%) TabTokenAccounts = 權杖 SectionRefresh = 自動化 +SectionRefreshing = 重新整理 +RefreshManualFooter = 自動重新整理已關閉;請使用系統匣選單中的「全部重新整理」。 SectionNotifications = 通知 +SectionAlerts = 提醒 +SessionQuotaNotifications = 額度耗盡與恢復 +SessionQuotaNotificationsHelper = 當提供者的工作階段額度剩餘 0% 以及重新可用時通知。 +WarningAtPercentUsed = 警告(已用 %) +CriticalAtPercentUsed = 嚴重(已用 %) +ThresholdsCaption = 工作階段和每週視窗使用的已用百分比,提供者可單獨覆寫。 +ThresholdOverridesTitle = 提供者臨界值 +ThresholdOverridesCaption = Codex 和 Claude 可以使用自己的警告和嚴重已用百分比。留空則使用上一層的值。 SectionUsageThresholds = 用量臨界值 SectionKeyboard = 鍵盤 SectionUsageRendering = 用量展示 @@ -506,6 +521,9 @@ SectionPreferencesTransfer = 可攜式偏好設定 PreferencesTransferCaption = 將顯示、重新整理、通知與提供者選項複製到另一台電腦。不包含 API 金鑰、Cookie、資料夾與主機。 PreferencesExportButton = 匯出偏好設定 PreferencesImportButton = 匯入偏好設定 +PreferencesTransferFooter = 將顯示和通知偏好儲存為檔案,可在另一台電腦上匯入。帳號、憑證、登入啟動、本機路徑和授權同意保留在這台電腦上。 +SwitcherShortcutsOpenButton = 提供者切換快捷鍵… +DialogDone = 完成 PreferencesExportSuccess = 偏好設定已匯出。 PreferencesImportSuccess = 偏好設定已匯入。 ShortcutRecordButton = 錄製 @@ -572,8 +590,9 @@ CodexLogPathsLabel = Codex 日誌路徑 CodexLogPathsHelper = 可以填寫 .codex 根目錄;應用程式會掃描其中的 sessions 資料夾。 AvoidKeychainPromptsLabel = 避免鑰匙串彈窗(Claude) AvoidKeychainPromptsHelper = 跳過 Claude 的鑰匙串憑據讀取,避免系統許可權彈窗。 -DisableAllKeychainLabel = 停用所有鑰匙串訪問 -DisableAllKeychainHelper = 關閉所有提供者的憑據/鑰匙串讀取。同時啟用上方的 Claude 選項。 +DisableAllKeychainLabel = 停用認證管理員存取 +DisableAllKeychainHelper = Windows 上尚未生效:CodexBar 仍會使用認證管理員、DPAPI 和瀏覽器 Cookie。此選項會儲存,待封鎖功能上線後使用。 +CredentialAccessFooter = 停用認證管理員存取期間,Claude 鑰匙串提示選項保持鎖定。 SectionTheme = 外觀 ThemeLabel = 主題 ThemeHelper = 自動跟隨系統配色方案;淺色/深色可手動覆蓋。 From 5ee879ef1835a2022f36701852e846b56307b86a Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:14:37 +0700 Subject: [PATCH 03/12] Add grouped settings controls: switch, menu select, SettingsGroup --- .../src/components/FormControls.tsx | 39 ++- .../src/surfaces/settings/settings-layout.css | 289 ++++++++++++++++++ 2 files changed, 326 insertions(+), 2 deletions(-) diff --git a/apps/desktop-tauri/src/components/FormControls.tsx b/apps/desktop-tauri/src/components/FormControls.tsx index 61be029a71..61fa4e37c5 100644 --- a/apps/desktop-tauri/src/components/FormControls.tsx +++ b/apps/desktop-tauri/src/components/FormControls.tsx @@ -20,18 +20,21 @@ export function Toggle({ label, ariaLabel, disabled, + variant = "checkbox", }: { checked: boolean; onChange: (v: boolean) => void; label?: string; ariaLabel?: string; disabled?: boolean; + /** "switch" draws the macOS-style switch used by the grouped settings panes. */ + variant?: "checkbox" | "switch"; }) { const fieldAria = useFieldAria(ariaLabel); const input = ( ); } + +// ── grouped section (macOS grouped Form look) ─────────────────────── + +/** A bold section title, a rounded box of rows with dividers, and an optional footer. */ +export function SettingsGroup({ + title, + footer, + children, +}: { + title?: string; + footer?: React.ReactNode; + children: React.ReactNode; +}) { + const id = useId(); + return ( +
+ {title && ( +

+ {title} +

+ )} +
{children}
+ {footer &&
{footer}
} +
+ ); +} diff --git a/apps/desktop-tauri/src/surfaces/settings/settings-layout.css b/apps/desktop-tauri/src/surfaces/settings/settings-layout.css index c0d314e750..06c86c0e07 100644 --- a/apps/desktop-tauri/src/surfaces/settings/settings-layout.css +++ b/apps/desktop-tauri/src/surfaces/settings/settings-layout.css @@ -57,3 +57,292 @@ .codex-accounts .credential-card__badge { align-self: flex-start; } + +/* ── Grouped panes (General, Notifications, Advanced, About) ───────── + macOS grouped Form: bold sentence-case title, one rounded box of rows + separated by 1px dividers, and an optional muted footer. */ +.settings-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.settings-group + .settings-group { + margin-top: 20px; +} + +.settings-group__title { + margin: 0 0 0 12px; + font-size: 13px; + font-weight: 650; + color: var(--text-primary); +} + +.settings-group__box { + display: flex; + flex-direction: column; + padding: 0 12px; + border: 1px solid var(--panel-border); + border-radius: 6px; + background: var(--surface-elevated); +} + +.settings-group__box > .settings-field, +.settings-group__box > .settings-group__row { + padding: 10px 0; +} + +.settings-group__box > * + * { + border-top: 1px solid var(--panel-border); +} + +.settings-group__box .settings-field__label { + font-size: 13px; +} + +.settings-group__box .settings-field__desc { + font-size: 12px; + color: var(--text-secondary); +} + +.settings-group__row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} + +.settings-group__row--split { + justify-content: space-between; +} + +.settings-group__footer { + margin: 0 12px; + font-size: 12px; + line-height: 1.4; + color: var(--text-secondary); +} + +.settings-group__footer p { + margin: 0; +} + +.settings-group__error { + margin: 0; + padding: 8px 0; + font-size: 12px; + color: var(--provider-status-error); +} + +/* Footer row of General: running version on the left, Quit on the right. */ +.settings-pane-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-top: 20px; + font-size: 12px; + color: var(--text-secondary); +} + +/* ── Switch toggle ────────────────────────────────────────────────── */ +.toggle.toggle--switch { + width: 32px; + height: 18px; + border-radius: 9px; + border: 1px solid rgba(255, 255, 255, 0.16); + background: rgba(255, 255, 255, 0.16); + transition: background 0.15s, border-color 0.15s; +} + +.toggle.toggle--switch::after { + content: ""; + position: absolute; + top: 1px; + left: 1px; + width: 14px; + height: 14px; + border: none; + border-radius: 50%; + background: #e8e8ea; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3); + transform: none; + transition: left 0.15s; +} + +.toggle.toggle--switch:checked { + background: var(--accent, #0a84ff); + border-color: var(--accent, #0a84ff); +} + +.toggle.toggle--switch:checked::after { + left: 15px; + width: 14px; + height: 14px; + border: none; + background: #fff; + transform: none; +} + +[data-theme="light"] .toggle.toggle--switch { + border-color: rgba(0, 0, 0, 0.12); + background: rgba(0, 0, 0, 0.12); +} + +[data-theme="light"] .toggle.toggle--switch:checked { + background: var(--accent, #007aff); + border-color: var(--accent, #007aff); +} + +/* ── Menu-style select: borderless "Value ⌄" ──────────────────────── */ +.select.select--menu { + border-color: transparent; + background-color: transparent; + text-align: right; + text-align-last: right; + font-size: 13px; +} + +.select.select--menu:hover:not(:disabled), +.select.select--menu:focus-visible { + border-color: var(--panel-border); + background-color: var(--surface-elevated); +} + +/* ── About hero ───────────────────────────────────────────────────── */ +.about-hero { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + padding: 20px 12px; + border: 1px solid var(--panel-border); + border-radius: 6px; + background: var(--surface-elevated); + text-align: center; +} + +.about-hero .about-icon { + width: 64px; + height: 64px; + margin-bottom: 8px; +} + +.about-hero__name { + margin: 0; + font-size: 16px; + font-weight: 650; + color: var(--text-primary); +} + +.about-hero__line { + margin: 0; + font-size: 12px; + color: var(--text-secondary); +} + +.about-hero + .settings-group { + margin-top: 20px; +} + +.settings-link-row { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: 10px 0; + border: none; + border-radius: 0; + background: transparent; + color: var(--text-primary); + font: inherit; + font-size: 13px; + text-align: left; + cursor: pointer; +} + +.settings-group__box > .settings-link-row + .settings-link-row { + border-top: 1px solid var(--panel-border); +} + +.settings-link-row:hover { + color: var(--accent); +} + +.settings-link-row:focus-visible { + outline: 2px solid var(--accent); + outline-offset: -2px; +} + +.settings-link-row__arrow { + color: var(--text-secondary); +} + +/* ── Switcher shortcuts dialog ────────────────────────────────────── */ +.settings-dialog-backdrop { + position: fixed; + inset: 0; + z-index: 50; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + background: rgba(0, 0, 0, 0.45); +} + +.settings-dialog { + display: flex; + flex-direction: column; + width: min(560px, 100%); + max-height: calc(100vh - 32px); + border: 1px solid var(--panel-border); + border-radius: 6px; + background: var(--app-bg, #1e1e1e); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45); +} + +.settings-dialog__header, +.settings-dialog__actions { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 12px 16px; +} + +.settings-dialog__header { + border-bottom: 1px solid var(--panel-border); +} + +.settings-dialog__title { + margin: 0; + font-size: 13px; + font-weight: 650; + color: var(--text-primary); +} + +.settings-dialog__body { + overflow-y: auto; + padding: 0 16px; +} + +.settings-dialog__actions { + justify-content: flex-end; + border-top: 1px solid var(--panel-border); +} + +.switcher-shortcuts { + display: flex; + flex-direction: column; + gap: 8px; + padding: 12px 0; +} + +.switcher-shortcuts .settings-section__caption { + margin: 0; +} + +.switcher-shortcuts__reset { + display: flex; + justify-content: flex-end; +} From b5dde123324d5507aca4e810e3139acd909ea9dc Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:14:37 +0700 Subject: [PATCH 04/12] Move General, Notifications, Advanced and About to the Mac layout - General: System, Refreshing, Portable preferences, Keyboard shortcut, footer with version and Quit CodexBar - Provider switcher shortcuts move from the Menu tab into a dialog - Notifications: Alerts group with Quota depleted & restored; thresholds and overrides show only while threshold warnings are on - Advanced: Privacy first; keyboard and portable preferences moved out - About: hero with build date, Updates and Links groups --- .../settings/SwitcherShortcutsDialog.test.tsx | 60 ++ .../settings/SwitcherShortcutsDialog.tsx | 69 ++ .../settings/SwitcherShortcutsSection.tsx | 29 +- .../surfaces/settings/tabs/AboutTab.test.tsx | 71 ++ .../src/surfaces/settings/tabs/AboutTab.tsx | 175 +++-- .../settings/tabs/AdvancedTab.test.tsx | 53 ++ .../surfaces/settings/tabs/AdvancedTab.tsx | 510 +++++-------- .../settings/tabs/DisplayTab.test.tsx | 8 + .../src/surfaces/settings/tabs/DisplayTab.tsx | 5 - .../settings/tabs/GeneralTab.test.tsx | 240 +++++- .../src/surfaces/settings/tabs/GeneralTab.tsx | 713 ++++++++++-------- .../tabs/PreferencesTransferSection.test.tsx | 24 + .../tabs/PreferencesTransferSection.tsx | 43 +- 13 files changed, 1275 insertions(+), 725 deletions(-) create mode 100644 apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.test.tsx create mode 100644 apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.tsx diff --git a/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.test.tsx b/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.test.tsx new file mode 100644 index 0000000000..3471ede791 --- /dev/null +++ b/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.test.tsx @@ -0,0 +1,60 @@ +import { fireEvent, render, screen } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; + +vi.mock("../../hooks/useLocale", () => ({ + useLocale: () => ({ t: (key: string) => key, language: "english" }), +})); + +import SwitcherShortcutsDialog from "./SwitcherShortcutsDialog"; +import type { SettingsSnapshot } from "../../types/bridge"; + +function renderDialog(onClose = vi.fn(), set = vi.fn()) { + const opener = document.createElement("button"); + document.body.appendChild(opener); + opener.focus(); + const view = render( + , + ); + return { ...view, opener, onClose, set }; +} + +describe("SwitcherShortcutsDialog", () => { + it("is a labelled modal dialog that takes focus and returns it on close", () => { + const { opener, unmount } = renderDialog(); + const dialog = screen.getByRole("dialog", { name: "SwitcherShortcutsTitle" }); + expect(dialog).toHaveAttribute("aria-modal", "true"); + expect(dialog).toHaveFocus(); + + unmount(); + expect(opener).toHaveFocus(); + opener.remove(); + }); + + it("closes on Escape, on Done and on a backdrop click", () => { + const { onClose, container, opener } = renderDialog(); + fireEvent.keyDown(screen.getByRole("dialog"), { key: "Escape" }); + fireEvent.click(screen.getByRole("button", { name: "DialogDone" })); + fireEvent.mouseDown(container.querySelector(".settings-dialog-backdrop") as Element); + expect(onClose).toHaveBeenCalledTimes(3); + opener.remove(); + }); + + it("does not close when the press starts inside the sheet", () => { + const { onClose, opener } = renderDialog(); + fireEvent.mouseDown(screen.getByRole("dialog")); + expect(onClose).not.toHaveBeenCalled(); + opener.remove(); + }); + + it("restores defaults through the section's Restore Defaults button", () => { + const { set, opener } = renderDialog(); + fireEvent.click(screen.getByRole("button", { name: "SwitcherShortcutReset" })); + expect(set).toHaveBeenCalledWith({ switcherShortcuts: {} }); + opener.remove(); + }); +}); diff --git a/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.tsx b/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.tsx new file mode 100644 index 0000000000..86f1414b3f --- /dev/null +++ b/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.tsx @@ -0,0 +1,69 @@ +import { useEffect, useId, useRef } from "react"; +import { useLocale } from "../../hooks/useLocale"; +import SwitcherShortcutsSection from "./SwitcherShortcutsSection"; +import type { TabProps } from "./settingsTabs"; + +/** + * In-window sheet for the provider-switcher keys, opened from + * Settings > General > Portable preferences. Escape or Done closes it and + * focus returns to the button that opened it. Every change saves at once, + * like the rest of Settings. + */ +export default function SwitcherShortcutsDialog({ + settings, + set, + saving, + onClose, +}: TabProps & { onClose: () => void }) { + const { t } = useLocale(); + const titleId = useId(); + const dialogRef = useRef(null); + + useEffect(() => { + const opener = document.activeElement as HTMLElement | null; + dialogRef.current?.focus(); + return () => opener?.focus?.(); + }, []); + + return ( +
{ + if (event.target === event.currentTarget) onClose(); + }} + > +
{ + if (event.key === "Escape") { + event.stopPropagation(); + onClose(); + } + }} + > +
+

+ {t("SwitcherShortcutsTitle")} +

+
+
+ +
+
+ +
+
+
+ ); +} diff --git a/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsSection.tsx b/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsSection.tsx index 16cf8c8654..fd1eb6350e 100644 --- a/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsSection.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsSection.tsx @@ -23,7 +23,7 @@ const ERROR_KEYS: Record = { invalid: "SwitcherShortcutErrorInvalid", }; -/** Editor for the provider-switcher keys (Settings > Menu). */ +/** Editor for the provider-switcher keys, shown in SwitcherShortcutsDialog (Settings > General). */ export default function SwitcherShortcutsSection({ settings, set, @@ -59,10 +59,9 @@ export default function SwitcherShortcutsSection({ }; return ( -
-

{t("SwitcherShortcutsTitle")}

+

{t("SwitcherShortcutsHelper")}

-
+
{SWITCHER_ACTIONS.map((action) => { const value = mapping[action]; return ( @@ -82,18 +81,20 @@ export default function SwitcherShortcutsSection({ })}
{error && ( -

+

{label(error.action)}: {t(ERROR_KEYS[error.code])}

)} - -
+
+ +
+ ); } diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.test.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.test.tsx index 7634e0146e..3a930e1efe 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.test.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.test.tsx @@ -159,3 +159,74 @@ describe("AboutTab", () => { }); }); }); + +describe("AboutTab Mac grouped layout", () => { + beforeEach(() => { + vi.clearAllMocks(); + tauriMocks.openExternalUrl.mockResolvedValue(undefined); + }); + + it("shows the build date in the hero when the shell reports one", async () => { + tauriMocks.getAppInfo.mockResolvedValue({ + name: "CodexBar", + version: "0.70.0", + buildNumber: "7", + buildDate: "2026-10-11", + updateChannel: "stable", + tagline: "Keep agent limits in view.", + }); + render(); + + expect(await screen.findByText("AboutBuilt")).toBeInTheDocument(); + expect(screen.getByText("Version 0.70.0 (7)")).toBeInTheDocument(); + }); + + it("leaves the build line out when no build date is known", async () => { + tauriMocks.getAppInfo.mockResolvedValue({ + name: "CodexBar", + version: "0.70.0", + buildNumber: "dev", + updateChannel: "stable", + tagline: "Keep agent limits in view.", + }); + render(); + + await screen.findByText("Keep agent limits in view."); + expect(screen.queryByText("AboutBuilt")).toBeNull(); + }); + + it("groups Updates then Links, with Submit an Issue as a link row", async () => { + tauriMocks.getAppInfo.mockResolvedValue({ + name: "CodexBar", + version: "0.70.0", + buildNumber: "dev", + updateChannel: "stable", + tagline: "Keep agent limits in view.", + }); + const { container } = render( + , + ); + await screen.findByText("Keep agent limits in view."); + + const titles = Array.from( + container.querySelectorAll(".settings-group__title"), + (node) => node.textContent, + ); + expect(titles).toEqual(["UpdatesTitle", "SectionLinks"]); + const linkRows = Array.from( + container.querySelectorAll(".settings-link-row"), + (node) => node.textContent, + ); + expect(linkRows).toEqual([ + "AboutLinkGitHub↗", + "AboutLinkWebsite↗", + "AboutLinkOriginalProject↗", + "SubmitIssue↗", + ]); + expect(screen.getByRole("checkbox", { name: "AutoDownloadUpdates" })).toHaveClass( + "toggle--switch", + ); + expect(screen.getByRole("combobox", { name: "UpdateChannelChoice" })).toHaveValue("stable"); + expect(screen.getByRole("button", { name: "AboutCheckForUpdates" })).toBeEnabled(); + }); +}); diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.tsx index c6e62e429c..cd4eaa278a 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { useLocale } from "../../../hooks/useLocale"; import { useUpdateState } from "../../../hooks/useUpdateState"; import { getAppInfo, openExternalUrl } from "../../../lib/tauri"; -import { Field, Select, Toggle } from "../../../components/FormControls"; +import { Field, Select, SettingsGroup, Toggle } from "../../../components/FormControls"; import type { AppInfoBridge, UpdateChannel } from "../../../types/bridge"; import type { LocaleKey } from "../../../i18n/keys"; import type { TabProps } from "../settingsTabs"; @@ -24,6 +24,10 @@ const ABOUT_LINKS: ReadonlyArray<{ labelKey: LocaleKey; url: string }> = [ labelKey: "AboutLinkOriginalProject", url: "https://github.com/steipete/CodexBar", }, + { + labelKey: "SubmitIssue", + url: SUBMIT_ISSUE_URL, + }, ]; export default function AboutTab({ settings, set, saving }: TabProps) { @@ -68,91 +72,69 @@ export default function AboutTab({ settings, set, saving }: TabProps) { const copyrightAfter = t("AboutCopyrightAfter"); return ( -
-
+
+
{t("AppName")} -
-

{appInfo.name}

-

- {t("Version")} {appInfo.version} - {appInfo.buildNumber !== "dev" && ` (${appInfo.buildNumber})`} +

{appInfo.name}

+

+ {t("Version")} {appInfo.version} + {appInfo.buildNumber !== "dev" && ` (${appInfo.buildNumber})`} +

+ {appInfo.buildDate && ( +

+ {t("AboutBuilt").replace("{}", appInfo.buildDate)}

-

{appInfo.tagline}

-
+ )} +

{appInfo.tagline}

-
- {ABOUT_LINKS.map((link) => ( - - ))} -
- - {linkError && ( -

- {t("ErrorPrefix")} {linkError} -

- )} - -
- -
+ set({ autoDownloadUpdates: v })} /> - -
- - set({ updateChannel: v as UpdateChannel })} + /> + +
+ + {t("Version")} {appInfo.version} + + +
{updateState.status === "available" && ( -
+
{t("UpdateAvailableMessage").replace( "{}", @@ -176,15 +158,17 @@ export default function AboutTab({ settings, set, saving }: TabProps) { )} {updateState.status === "downloading" && ( - - {t("UpdateDownloading")} - {updateState.progress != null && - ` ${Math.round(updateState.progress * 100)}%`} - +
+ + {t("UpdateDownloading")} + {updateState.progress != null && + ` ${Math.round(updateState.progress * 100)}%`} + +
)} {updateState.status === "ready" && ( -
+
{t("UpdateReady")} {updateState.canApply ? (
+ + + + {t("ErrorPrefix")} {linkError} +

+ ) : undefined + } + > + {ABOUT_LINKS.map((link) => ( + + ))} +

{copyrightBefore}{" "} @@ -224,6 +237,6 @@ export default function AboutTab({ settings, set, saving }: TabProps) { {" "}{copyrightAfter}

-
+ ); } diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/AdvancedTab.test.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/AdvancedTab.test.tsx index 2bf33797de..a59cc74341 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/AdvancedTab.test.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/AdvancedTab.test.tsx @@ -209,3 +209,56 @@ describe("AdvancedTab", () => { expect(set).toHaveBeenCalledWith({ stayAwakeEnabled: true }); }); }); + +describe("AdvancedTab Mac grouped layout", () => { + it("orders the groups with Privacy first and no keyboard or portable preferences", () => { + const { container } = render( + , + ); + const titles = Array.from( + container.querySelectorAll(".settings-group__title"), + (node) => node.textContent, + ); + expect(titles).toEqual([ + "PrivacyTitle", + "CodexLocalLogsTitle", + "AgentSessionsTitle", + "SectionLocalIntegrations", + "NetworkProxyTitle", + "HooksTitle", + "DiagnosticsSectionHeading", + ]); + expect(screen.queryByText("SectionKeyboard")).toBeNull(); + expect(screen.queryByText("SectionPreferencesTransfer")).toBeNull(); + expect(screen.queryByText("KEYCHAIN ACCESS")).toBeNull(); + }); + + it("draws every toggle as a switch", () => { + render(); + for (const toggle of screen.getAllByRole("checkbox")) { + expect(toggle).toHaveClass("toggle--switch"); + } + }); + + it("locks the Claude keychain-prompt option while Credential Manager access is off", () => { + const set = vi.fn(); + const { rerender } = render( + , + ); + expect(screen.getByRole("checkbox", { name: "AvoidKeychainPromptsLabel" })).toBeEnabled(); + expect(screen.queryByText("CredentialAccessFooter")).toBeNull(); + + fireEvent.click(screen.getByRole("checkbox", { name: "DisableAllKeychainLabel" })); + expect(set).toHaveBeenCalledWith({ disableKeychainAccess: true }); + + rerender( + , + ); + expect(screen.getByRole("checkbox", { name: "AvoidKeychainPromptsLabel" })).toBeDisabled(); + expect(screen.getByText("CredentialAccessFooter")).toBeInTheDocument(); + }); +}); diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/AdvancedTab.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/AdvancedTab.tsx index 88b540e8de..87a376816f 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/AdvancedTab.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/AdvancedTab.tsx @@ -1,14 +1,8 @@ import { useCallback, useEffect, useState } from "react"; import { useLocale } from "../../../hooks/useLocale"; -import { - getSafeDiagnostics, - registerGlobalShortcut, - unregisterGlobalShortcut, -} from "../../../lib/tauri"; -import { ShortcutCapture } from "../../../components/ShortcutCapture"; -import { Field, Toggle } from "../../../components/FormControls"; +import { getSafeDiagnostics } from "../../../lib/tauri"; +import { Field, SettingsGroup, Toggle } from "../../../components/FormControls"; import type { TabProps } from "../settingsTabs"; -import PreferencesTransferSection from "./PreferencesTransferSection"; function formatCodexSessionsDirs(paths: string[]): string { return paths.join("; "); @@ -27,7 +21,6 @@ function parseSshHosts(value: string): string[] { export default function AdvancedTab({ settings, set, saving }: TabProps) { const { t } = useLocale(); - const [shortcutError, setShortcutError] = useState(null); const [diagnosticsStatus, setDiagnosticsStatus] = useState( null, ); @@ -99,303 +92,219 @@ export default function AdvancedTab({ settings, set, saving }: TabProps) { } }, [proxyPasswordDraft, set, settings.httpProxyPassword]); - const commitShortcut = useCallback( - async (accelerator: string) => { - setShortcutError(null); - try { - await registerGlobalShortcut(accelerator).catch(() => {}); - set({ globalShortcut: accelerator }); - } catch (err: unknown) { - setShortcutError(err instanceof Error ? err.message : String(err)); - } - }, - [set], - ); - - const clearShortcut = useCallback(async () => { - setShortcutError(null); - try { - await unregisterGlobalShortcut().catch(() => {}); - set({ globalShortcut: "" }); - } catch (err: unknown) { - setShortcutError(err instanceof Error ? err.message : String(err)); - } - }, [set]); - - return ( <> - {/* ── Keyboard shortcut ────────────────────────────────────── */} -
-

{t("SectionKeyboard")}

-
- - void commitShortcut(accel)} - onClear={() => void clearShortcut()} - /> - -
- {shortcutError && ( -

{shortcutError}

- )} -

{t("ShortcutRecordingHint")}

-
- - {/* -- Codex local logs -------------------------------------- */} -
-

- {t("CodexLocalLogsTitle")} -

-

- {t("CodexLocalLogsCaption")} -

-
- - \home\\.codex`} - disabled={saving} - onChange={(event) => setCodexDirsDraft(event.target.value)} - onBlur={commitCodexDirs} - onKeyDown={(event) => { - if (event.key === "Enter") { - event.currentTarget.blur(); - } - }} - /> - -
-
- - {/* -- Privacy ----------------------------------------------- */} -
-

{t("AgentSessionsTitle")}

-
- - set({ agentSessionsEnabled: v })} - /> - - - set({ stayAwakeEnabled: v })} - /> - - - setSshHostsDraft(event.target.value)} - onBlur={() => set({ agentSessionSshHosts: parseSshHosts(sshHostsDraft) })} - onKeyDown={(event) => { - if (event.key === "Enter") event.currentTarget.blur(); - }} - /> - -
-
+ {t("CredentialAccessFooter")}

: undefined + } + > + + set({ hidePersonalInfo: v })} + /> + + + set({ disableKeychainAccess: v })} + /> + + + set({ claudeAvoidKeychainPrompts: v })} + /> + +
- {/* -- Privacy ----------------------------------------------- */} -
-

{t("PrivacyTitle")}

-
- - set({ hidePersonalInfo: v })} - /> - -
-
+ {t("CodexLocalLogsCaption")}

} + > + + \home\\.codex`} + disabled={saving} + onChange={(event) => setCodexDirsDraft(event.target.value)} + onBlur={commitCodexDirs} + onKeyDown={(event) => { + if (event.key === "Enter") { + event.currentTarget.blur(); + } + }} + /> + +
- {/* -- Local integrations ----------------------------------- */} -
-

- {t("SectionLocalIntegrations")} -

-
- - set({ powertoysStatusPipeEnabled: v })} - /> - -
-
+ + + set({ agentSessionsEnabled: v })} + /> + + + set({ stayAwakeEnabled: v })} + /> + + + setSshHostsDraft(event.target.value)} + onBlur={() => set({ agentSessionSshHosts: parseSshHosts(sshHostsDraft) })} + onKeyDown={(event) => { + if (event.key === "Enter") event.currentTarget.blur(); + }} + /> + + - {/* -- Network proxy ---------------------------------------- */} -
-

{t("NetworkProxyTitle")}

-

{t("NetworkProxyCaption")}

-
- - set({ httpProxyEnabled: v })} - /> - - - setProxyUrlDraft(event.target.value)} - onBlur={commitProxyUrl} - onKeyDown={(event) => { - if (event.key === "Enter") event.currentTarget.blur(); - }} - /> - - - setProxyUsernameDraft(event.target.value)} - onBlur={commitProxyUsername} - onKeyDown={(event) => { - if (event.key === "Enter") event.currentTarget.blur(); - }} - /> - - - setProxyPasswordDraft(event.target.value)} - onBlur={commitProxyPassword} - onKeyDown={(event) => { - if (event.key === "Enter") event.currentTarget.blur(); - }} - /> - -
-
- - {/* -- External hooks --------------------------------------- */} -
-

{t("HooksTitle")}

-

{t("HooksCaption")}

-
- - set({ hooksEnabled: v })} - /> - -
-

{t("HooksConfigPathHint")}

-
+ + + set({ powertoysStatusPipeEnabled: v })} + /> + + - {/* ── Keychain access ──────────────────────────────────────── */} -
-

- KEYCHAIN ACCESS -

-

- Disable all Keychain reads and writes. Browser cookie import is - unavailable; paste Cookie headers manually in Providers. -

-
- - set({ disableKeychainAccess: v })} - /> - - - set({ claudeAvoidKeychainPrompts: v })} - /> - -
-
+ {t("NetworkProxyCaption")}

} + > + + set({ httpProxyEnabled: v })} + /> + + + setProxyUrlDraft(event.target.value)} + onBlur={commitProxyUrl} + onKeyDown={(event) => { + if (event.key === "Enter") event.currentTarget.blur(); + }} + /> + + + setProxyUsernameDraft(event.target.value)} + onBlur={commitProxyUsername} + onKeyDown={(event) => { + if (event.key === "Enter") event.currentTarget.blur(); + }} + /> + + + setProxyPasswordDraft(event.target.value)} + onBlur={commitProxyPassword} + onKeyDown={(event) => { + if (event.key === "Enter") event.currentTarget.blur(); + }} + /> + +
- + +

{t("HooksCaption")}

+

{t("HooksConfigPathHint")}

+ + } + > + + set({ hooksEnabled: v })} + /> + +
- {/* ── Diagnostics ──────────────────────────────────────────── */} -
-

- {t("DiagnosticsSectionHeading")} -

-
+ {diagnosticsStatus}

: undefined} + > +
- {diagnosticsStatus && ( -

{diagnosticsStatus}

- )}
-
+ ); } diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/DisplayTab.test.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/DisplayTab.test.tsx index 8cff53314d..33f6ef1b4f 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/DisplayTab.test.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/DisplayTab.test.tsx @@ -158,3 +158,11 @@ describe("DisplayTab stacked tray providers", () => { expect(set).toHaveBeenCalledWith({ stackedTrayBottomProvider: "codex" }); }); }); + +describe("DisplayTab switcher shortcuts", () => { + it("no longer hosts the provider switcher shortcuts on the Menu tab", () => { + renderTab(vi.fn(), "menu"); + expect(screen.queryByText("SwitcherShortcutsTitle")).toBeNull(); + expect(screen.queryByRole("button", { name: "SwitcherShortcutReset" })).toBeNull(); + }); +}); diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/DisplayTab.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/DisplayTab.tsx index 79c6ed0aed..572b91beda 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/DisplayTab.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/DisplayTab.tsx @@ -11,7 +11,6 @@ import type { } from "../../../types/bridge"; import type { TabProps } from "../settingsTabs"; import FloatBarSettingsSection from "../../../floatbar/SettingsSection"; -import SwitcherShortcutsSection from "../SwitcherShortcutsSection"; import { getTrayVisibilityStatus } from "../../../lib/tauri"; import { clampTrayScalePercent, @@ -291,10 +290,6 @@ export default function DisplayTab({ } - {mode === "menu" && ( - - )} - {mode === "menu" && ( )} diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.test.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.test.tsx index 5185a6f884..30f640b935 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.test.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { fireEvent, render, screen, waitFor, within } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; vi.mock("../../../hooks/useLocale", () => ({ @@ -116,7 +116,7 @@ describe("GeneralTab running version", () => { it("shows the running app version on the general tab", async () => { render(); - expect(await screen.findByText("0.60.3 (42)")).toBeInTheDocument(); + expect(await screen.findByText("Version 0.60.3 (42)")).toBeInTheDocument(); }); }); @@ -409,3 +409,239 @@ describe("GeneralTab preferred currency picker", () => { expect(screen.queryByRole("combobox", { name: "ThemeLabel" })).toBeNull(); }); + +describe("GeneralTab Mac grouped layout", () => { + it("orders the groups System, Refreshing, Portable preferences, Keyboard shortcut", () => { + const { container } = render( + , + ); + const titles = Array.from( + container.querySelectorAll(".settings-group__title"), + (node) => node.textContent, + ); + expect(titles).toEqual([ + "StartupSettings", + "SectionRefreshing", + "SectionPreferencesTransfer", + "SectionKeyboard", + ]); + }); + + it("lists System rows in Mac order with Windows extras after them", () => { + const { container } = render( + , + ); + const system = container.querySelector(".settings-group") as HTMLElement; + const labels = Array.from( + system.querySelectorAll(".settings-field__label"), + (node) => node.textContent, + ); + expect(labels).toEqual([ + "InterfaceLanguage", + "PreferredCurrencyLabel", + "Appearance", + "StartAtLogin", + "StartMinimized", + ]); + const toggles = within(system).getAllByRole("checkbox"); + expect(toggles).toHaveLength(2); + for (const toggle of toggles) { + expect(toggle).toHaveClass("toggle--switch"); + } + }); + + it("offers the Mac refresh intervals plus the Windows 1 hour option", () => { + render(); + const select = screen.getByRole("combobox", { name: "RefreshIntervalLabel" }); + const values = Array.from( + select.querySelectorAll("option"), + (option) => option.getAttribute("value"), + ); + expect(values).toEqual(["0", "60", "120", "300", "900", "1800", "3600", "adaptive"]); + }); + + it("saves 2 min as a fixed interval and turns adaptive off", () => { + const set = vi.fn(); + render( + , + ); + fireEvent.change(screen.getByRole("combobox", { name: "RefreshIntervalLabel" }), { + target: { value: "120" }, + }); + expect(set).toHaveBeenCalledWith({ adaptiveRefresh: false, refreshIntervalSecs: 120 }); + }); + + it("keeps a saved interval that is not in the menu visible", () => { + render( + , + ); + const select = screen.getByRole("combobox", { name: "RefreshIntervalLabel" }); + expect(select).toHaveValue("45"); + expect(within(select).getByText("45 s")).toBeInTheDocument(); + }); + + it("shows the Manual footer only when auto-refresh is off", () => { + const { rerender } = render( + , + ); + expect(screen.queryByText("RefreshManualFooter")).toBeNull(); + + rerender( + , + ); + expect(screen.getByText("RefreshManualFooter")).toBeInTheDocument(); + + rerender( + , + ); + expect(screen.queryByText("RefreshManualFooter")).toBeNull(); + }); + + it("hosts the global shortcut recorder as Open menu", () => { + render(); + expect(screen.getByText("GlobalShortcutFieldLabel")).toBeInTheDocument(); + expect(screen.getByText("ShortcutRecordingHint")).toBeInTheDocument(); + }); + + it("quits from the footer button", () => { + render(); + fireEvent.click(screen.getByRole("button", { name: "QuitCodexBar" })); + expect(invoke).toHaveBeenCalledWith("quit_app"); + }); + + it("opens the provider switcher shortcuts dialog and closes it with Escape or Done", () => { + render(); + expect(screen.queryByRole("dialog")).toBeNull(); + + fireEvent.click(screen.getByRole("button", { name: "SwitcherShortcutsOpenButton" })); + const dialog = screen.getByRole("dialog", { name: "SwitcherShortcutsTitle" }); + expect( + within(dialog).getByRole("button", { name: "SwitcherShortcutReset" }), + ).toBeInTheDocument(); + + fireEvent.keyDown(dialog, { key: "Escape" }); + expect(screen.queryByRole("dialog")).toBeNull(); + + fireEvent.click(screen.getByRole("button", { name: "SwitcherShortcutsOpenButton" })); + fireEvent.click(screen.getByRole("button", { name: "DialogDone" })); + expect(screen.queryByRole("dialog")).toBeNull(); + }); +}); + +describe("GeneralTab notifications pane", () => { + it("orders the Alerts rows like the Mac pane", () => { + const { container } = render( + , + ); + const alerts = container.querySelector(".settings-group") as HTMLElement; + const labels = Array.from( + alerts.querySelectorAll(".settings-field__label"), + (node) => node.textContent, + ); + expect(labels).toEqual([ + "CredentialExpiryNotifications", + "SessionQuotaNotifications", + "ShowNotifications", + "PredictivePaceWarnings", + ]); + }); + + it("saves quota depleted and restored alerts as their own key", () => { + const set = vi.fn(); + render( + , + ); + const toggle = screen.getByRole("checkbox", { name: "SessionQuotaNotifications" }); + expect(toggle).toBeChecked(); + fireEvent.click(toggle); + expect(set).toHaveBeenCalledWith({ sessionQuotaNotificationsEnabled: false }); + }); + + it("falls back to show notifications when the quota key is absent", () => { + render( + , + ); + expect( + screen.getByRole("checkbox", { name: "SessionQuotaNotifications" }), + ).not.toBeChecked(); + }); + + it("locks credential and pace alerts and hides thresholds while usage warnings are off", () => { + render( + , + ); + const pace = screen.getByRole("checkbox", { name: "PredictivePaceWarnings" }); + const credential = screen.getByRole("checkbox", { name: "CredentialExpiryNotifications" }); + expect(pace).toBeDisabled(); + expect(pace).toBeChecked(); + expect(credential).toBeDisabled(); + expect(credential).toBeChecked(); + expect(screen.getByRole("checkbox", { name: "SessionQuotaNotifications" })).toBeEnabled(); + expect(screen.queryByText("WarningAtPercentUsed")).toBeNull(); + expect(screen.queryByText("ThresholdOverridesTitle")).toBeNull(); + expect(screen.getByRole("checkbox", { name: "SoundEnabled" })).toBeInTheDocument(); + }); + + it("shows % used thresholds and overrides while usage warnings are on", () => { + render( + , + ); + expect(screen.getByText("WarningAtPercentUsed")).toBeInTheDocument(); + expect(screen.getByText("CriticalAtPercentUsed")).toBeInTheDocument(); + expect(screen.getByText("ThresholdOverridesTitle")).toBeInTheDocument(); + expect(screen.getByText("ThresholdsCaption")).toBeInTheDocument(); + }); + + it("hides the sound set and per-event sounds while sound is off", () => { + render( + , + ); + expect(screen.queryByRole("combobox", { name: "NotificationSoundTheme" })).toBeNull(); + expect(screen.queryAllByRole("button", { name: /NotificationTestSound$/ })).toHaveLength(0); + }); +}); diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.tsx index e4c48b2d6e..a677350dfd 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.tsx @@ -2,9 +2,22 @@ import { useCallback, useEffect, useState } from "react"; import { useLocale } from "../../../hooks/useLocale"; import { invoke } from "@tauri-apps/api/core"; import { open } from "@tauri-apps/plugin-dialog"; -import { getAppInfo, playNotificationSound, quitApp } from "../../../lib/tauri"; +import { + getAppInfo, + playNotificationSound, + quitApp, + registerGlobalShortcut, + unregisterGlobalShortcut, +} from "../../../lib/tauri"; import type { AppInfoBridge } from "../../../types/bridge"; -import { Field, NumberInput, Select, Toggle } from "../../../components/FormControls"; +import { + Field, + NumberInput, + Select, + SettingsGroup, + Toggle, +} from "../../../components/FormControls"; +import { ShortcutCapture } from "../../../components/ShortcutCapture"; import type { Language, LanguageOption, @@ -17,6 +30,8 @@ import type { import type { LocaleKey } from "../../../i18n/keys"; import { CURRENCY_PICKER_OPTIONS } from "../../../lib/currency"; import type { TabProps } from "../settingsTabs"; +import PreferencesTransferSection from "./PreferencesTransferSection"; +import SwitcherShortcutsDialog from "../SwitcherShortcutsDialog"; const FALLBACK_LANGUAGE_OPTIONS: LanguageOption[] = [ { value: "english", display: "English" }, @@ -31,14 +46,17 @@ const FALLBACK_LANGUAGE_OPTIONS: LanguageOption[] = [ { value: "ukrainian", display: "Українська" }, ]; +// Mac order (Manual, minutes, Adaptive last) plus the Windows "1 hour", +// which existing installs may have saved. const REFRESH_CADENCE_OPTIONS: { value: string; labelKey: LocaleKey }[] = [ { value: "0", labelKey: "RefreshIntervalManual" }, - { value: "adaptive", labelKey: "RefreshIntervalAdaptive" }, { value: "60", labelKey: "RefreshInterval1Min" }, + { value: "120", labelKey: "RefreshInterval2Min" }, { value: "300", labelKey: "RefreshInterval5Min" }, { value: "900", labelKey: "RefreshInterval15Min" }, { value: "1800", labelKey: "RefreshInterval30Min" }, { value: "3600", labelKey: "RefreshInterval1Hour" }, + { value: "adaptive", labelKey: "RefreshIntervalAdaptive" }, ]; const NOTIFICATION_SOUND_THEME_OPTIONS: { @@ -109,7 +127,7 @@ const THEME_OPTIONS: { value: ThemePreference; labelKey: LocaleKey }[] = [ { value: "auto", labelKey: "ThemeAutoOption" }, { value: "light", labelKey: "ThemeLightOption" }, { value: "dark", labelKey: "ThemeDarkOption" }, -] +]; function fileName(path: string): string { return path.split(/[\\/]/).pop() ?? path; @@ -193,6 +211,8 @@ export default function GeneralTab({ const { t } = useLocale(); const [playingSound, setPlayingSound] = useState(null); const [soundError, setSoundError] = useState(null); + const [shortcutError, setShortcutError] = useState(null); + const [switcherOpen, setSwitcherOpen] = useState(false); const [languageOptions, setLanguageOptions] = useState( FALLBACK_LANGUAGE_OPTIONS, ); @@ -262,206 +282,409 @@ export default function GeneralTab({ [set, settings.notificationSoundPaths], ); + const commitShortcut = useCallback( + async (accelerator: string) => { + setShortcutError(null); + try { + await registerGlobalShortcut(accelerator).catch(() => {}); + set({ globalShortcut: accelerator }); + } catch (err: unknown) { + setShortcutError(err instanceof Error ? err.message : String(err)); + } + }, + [set], + ); + + const clearShortcut = useCallback(async () => { + setShortcutError(null); + try { + await unregisterGlobalShortcut().catch(() => {}); + set({ globalShortcut: "" }); + } catch (err: unknown) { + setShortcutError(err instanceof Error ? err.message : String(err)); + } + }, [set]); + + if (mode === "notifications") { + return ( + void handleChooseSound(pathKey)} + onClearSound={handleClearSound} + /> + ); + } + + const refreshValue = settings.adaptiveRefresh + ? "adaptive" + : String(settings.refreshIntervalSecs); + const refreshOptions = REFRESH_CADENCE_OPTIONS.map((o) => ({ + value: o.value, + label: t(o.labelKey), + })); + // A saved interval outside the menu (for example 30 s from an older build) + // stays visible instead of the menu silently showing another value. + if (!refreshOptions.some((o) => o.value === refreshValue)) { + refreshOptions.push({ + value: refreshValue, + label: `${settings.refreshIntervalSecs} s`, + }); + } + const manualRefresh = !settings.adaptiveRefresh && settings.refreshIntervalSecs === 0; + return ( <> - {mode === "general" &&
-

{t("SectionLanguage")}

-
- - ({ + value: opt.value, + label: opt.display, + }))} + onChange={(v) => set({ uiLanguage: v as Language })} + /> + + + ({ + value: option.value, + label: t(option.labelKey), + }))} + onChange={(value) => set({ theme: value as ThemePreference })} + /> + + + set({ startAtLogin: v })} + /> + + + set({ startMinimized: v })} + /> + + + + {t("RefreshManualFooter")}

: undefined} + > + + set({ + lowPowerModePreference: v as "off" | "on" | "automatic", + })} + /> + +
+ + setSwitcherOpen(true)} /> + + + {shortcutError && ( +

{shortcutError}

+ )} +

{t("ShortcutRecordingHint")}

+ + } + > + + void commitShortcut(accel)} + onClear={() => void clearShortcut()} + /> + +
+ +
+ + {appVersion && + `${t("Version")} ${appVersion.version}${ + appVersion.buildNumber !== "dev" ? ` (${appVersion.buildNumber})` : "" + }`} + + +
+ + {switcherOpen && ( + setSwitcherOpen(false)} + /> + )} + + ); +} + +function NotificationsPane({ + settings, + set, + saving, + playingSound, + soundError, + onTestSound, + onChooseSound, + onClearSound, +}: TabProps & { + playingSound: NotificationSoundEvent | null; + soundError: string | null; + onTestSound: (event: NotificationSoundEvent) => void; + onChooseSound: (pathKey: NotificationSoundPathKey) => void; + onClearSound: (pathKey: NotificationSoundPathKey) => void; +}) { + const { t } = useLocale(); + // `show_notifications` still gates credential and pace alerts in the + // backend, so their switches lock (keeping their saved state) while + // threshold warnings are off. Quota depleted & restored has its own key. + const alertsMuted = !settings.showNotifications; + const values = settings.providerUsageThresholds ?? {}; + + return ( + <> + + + set({ credentialExpiryNotificationsEnabled: v })} + /> + + + set({ sessionQuotaNotificationsEnabled: v })} + /> + + + set({ showNotifications: v })} + /> + + + set({ predictivePaceWarningEnabled: v })} + /> + + + + {settings.showNotifications && ( + {t("ThresholdsCaption")}

}> + + ({ - value: opt.value, - label: opt.display, - }))} - onChange={(v) => set({ uiLanguage: v as Language })} + onChange={(v) => set({ highUsageThreshold: v })} /> - - ({ + ariaLabel={t("NotificationSoundTheme")} + minWidth={NOTIFICATION_SOUND_THEME_SELECT_MIN_WIDTH} + options={NOTIFICATION_SOUND_THEME_OPTIONS.map((option) => ({ value: option.value, label: t(option.labelKey), }))} - onChange={(value) => set({ theme: value as ThemePreference })} - /> - -
-
} - {mode === "general" &&
-

{t("StartupSettings")}

-
- - set({ startAtLogin: v })} - /> - - - set({ startMinimized: v })} - /> - -
-
} - - {mode === "notifications" &&
-

- {t("SectionNotifications")} -

-
- - set({ showNotifications: v })} - /> - - - set({ predictivePaceWarningEnabled: v })} - /> - - - set({ credentialExpiryNotificationsEnabled: v })} - /> - - - set({ soundEnabled: v })} + onChange={(value) => { + if (isNotificationSoundTheme(value)) { + set({ notificationSoundTheme: value }); + } + }} /> - {settings.soundEnabled && ( - <> - - ({ - value: o.value, - label: t(o.labelKey), - }))} - onChange={(v) => { - if (v === "adaptive") { - set({ adaptiveRefresh: true }); - return; - } - set({ - adaptiveRefresh: false, - refreshIntervalSecs: Number(v), - }); - }} - /> - - - set({ refreshAllProvidersOnMenuOpen: v })} - /> - - - Date: Sun, 11 Oct 2026 11:38:41 +0700 Subject: [PATCH 06/12] Read the build date with option_env so a reused build script cannot break the build --- apps/desktop-tauri/src-tauri/src/commands/credentials.rs | 6 ++++-- apps/desktop-tauri/src-tauri/src/commands/system.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/desktop-tauri/src-tauri/src/commands/credentials.rs b/apps/desktop-tauri/src-tauri/src/commands/credentials.rs index 2296258e13..cd4b9e916e 100644 --- a/apps/desktop-tauri/src-tauri/src/commands/credentials.rs +++ b/apps/desktop-tauri/src-tauri/src/commands/credentials.rs @@ -40,8 +40,10 @@ pub struct AppInfoBridge { pub name: String, pub version: String, pub build_number: String, - /// UTC date this binary was built (YYYY-MM-DD). - pub build_date: String, + /// UTC date this binary was built (YYYY-MM-DD). `None` when the build + /// script output was reused from a target dir shared with another branch. + #[serde(skip_serializing_if = "Option::is_none")] + pub build_date: Option, pub update_channel: String, pub tagline: String, } diff --git a/apps/desktop-tauri/src-tauri/src/commands/system.rs b/apps/desktop-tauri/src-tauri/src/commands/system.rs index c70e75c0f2..c6eb10e685 100644 --- a/apps/desktop-tauri/src-tauri/src/commands/system.rs +++ b/apps/desktop-tauri/src-tauri/src/commands/system.rs @@ -7,7 +7,7 @@ pub fn get_app_info() -> AppInfoBridge { name: "CodexBar".to_string(), version: env!("CARGO_PKG_VERSION").to_string(), build_number: option_env!("BUILD_NUMBER").unwrap_or("dev").to_string(), - build_date: env!("CODEXBAR_BUILD_DATE").to_string(), + build_date: option_env!("CODEXBAR_BUILD_DATE").map(str::to_string), update_channel: update_channel_label(settings.update_channel).to_string(), tagline: "May your tokens never run out—keep agent limits in view.".to_string(), } From 327f83ae22515690a00091295b400c1baae96330 Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Sun, 11 Oct 2026 12:04:19 +0700 Subject: [PATCH 07/12] Keep Tab focus inside the switcher shortcuts dialog --- .../settings/SwitcherShortcutsDialog.test.tsx | 33 +++++++++++++++++++ .../settings/SwitcherShortcutsDialog.tsx | 30 +++++++++++++++-- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.test.tsx b/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.test.tsx index 3471ede791..d9001819a2 100644 --- a/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.test.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.test.tsx @@ -35,6 +35,39 @@ describe("SwitcherShortcutsDialog", () => { opener.remove(); }); + it("keeps Tab and Shift+Tab inside the sheet", () => { + const { onClose, opener } = renderDialog(); + const dialog = screen.getByRole("dialog"); + const done = screen.getByRole("button", { name: "DialogDone" }); + const firstRecord = screen.getByRole("button", { + name: "SwitcherShortcutPrevious: ShortcutRecordButton", + }); + + // Tab from the last control wraps to the first one. + done.focus(); + fireEvent.keyDown(done, { key: "Tab" }); + expect(firstRecord).toHaveFocus(); + + // Shift+Tab from the first control wraps to Done. + fireEvent.keyDown(firstRecord, { key: "Tab", shiftKey: true }); + expect(done).toHaveFocus(); + + // From the sheet itself (focus on open), Shift+Tab lands on Done and Tab on the first control. + dialog.focus(); + fireEvent.keyDown(dialog, { key: "Tab", shiftKey: true }); + expect(done).toHaveFocus(); + dialog.focus(); + fireEvent.keyDown(dialog, { key: "Tab" }); + expect(firstRecord).toHaveFocus(); + + // A Tab between two inner controls is left to the browser. + const event = new KeyboardEvent("keydown", { key: "Tab", bubbles: true, cancelable: true }); + firstRecord.dispatchEvent(event); + expect(event.defaultPrevented).toBe(false); + expect(onClose).not.toHaveBeenCalled(); + opener.remove(); + }); + it("closes on Escape, on Done and on a backdrop click", () => { const { onClose, container, opener } = renderDialog(); fireEvent.keyDown(screen.getByRole("dialog"), { key: "Escape" }); diff --git a/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.tsx b/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.tsx index 86f1414b3f..7d2dd0082d 100644 --- a/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/SwitcherShortcutsDialog.tsx @@ -1,12 +1,36 @@ +import type React from "react"; import { useEffect, useId, useRef } from "react"; import { useLocale } from "../../hooks/useLocale"; import SwitcherShortcutsSection from "./SwitcherShortcutsSection"; import type { TabProps } from "./settingsTabs"; +const FOCUSABLE = + 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'; + +/** Keeps Tab and Shift+Tab inside the modal sheet. */ +function trapTab(dialog: HTMLElement, event: React.KeyboardEvent) { + const focusable = Array.from(dialog.querySelectorAll(FOCUSABLE)); + if (focusable.length === 0) { + event.preventDefault(); + return; + } + const first = focusable[0]; + const last = focusable[focusable.length - 1]; + const active = document.activeElement; + const outside = !focusable.includes(active as HTMLElement); + if (event.shiftKey && (active === first || outside)) { + event.preventDefault(); + last.focus(); + } else if (!event.shiftKey && (active === last || outside)) { + event.preventDefault(); + first.focus(); + } +} + /** * In-window sheet for the provider-switcher keys, opened from - * Settings > General > Portable preferences. Escape or Done closes it and - * focus returns to the button that opened it. Every change saves at once, + * Settings > General > Portable preferences. Tab stays inside the sheet, + * Escape or Done closes it and focus returns to the button that opened it. Every change saves at once, * like the rest of Settings. */ export default function SwitcherShortcutsDialog({ @@ -43,6 +67,8 @@ export default function SwitcherShortcutsDialog({ if (event.key === "Escape") { event.stopPropagation(); onClose(); + } else if (event.key === "Tab" && dialogRef.current) { + trapTab(dialogRef.current, event); } }} > From 5b34472c4dfee25eef74513422c5558fc1041b18 Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Sun, 11 Oct 2026 12:04:19 +0700 Subject: [PATCH 08/12] Say the Claude keychain-prompt option is not enforced on Windows yet --- rust/src/locale/en-US.ftl | 2 +- rust/src/locale/es-MX.ftl | 2 +- rust/src/locale/ja-JP.ftl | 2 +- rust/src/locale/ko-KR.ftl | 2 +- rust/src/locale/pt-BR.ftl | 2 +- rust/src/locale/ru-RU.ftl | 2 +- rust/src/locale/tests.rs | 56 +++++++++++++++++++++++++++++++++++++++ rust/src/locale/tr-TR.ftl | 2 +- rust/src/locale/uk-UA.ftl | 2 +- rust/src/locale/zh-CN.ftl | 2 +- rust/src/locale/zh-TW.ftl | 2 +- 11 files changed, 66 insertions(+), 10 deletions(-) diff --git a/rust/src/locale/en-US.ftl b/rust/src/locale/en-US.ftl index 17d9407d3b..80938df2bc 100644 --- a/rust/src/locale/en-US.ftl +++ b/rust/src/locale/en-US.ftl @@ -616,7 +616,7 @@ CodexLocalLogsCaption = Add Codex home or sessions directories for local cost es CodexLogPathsLabel = Codex log paths CodexLogPathsHelper = A .codex root is accepted; it will scan the sessions folder inside it. AvoidKeychainPromptsLabel = Avoid keychain prompts (Claude) -AvoidKeychainPromptsHelper = Skip keychain credential reads for Claude to prevent OS permission dialogs. +AvoidKeychainPromptsHelper = Not enforced on Windows yet: Claude credentials are still read as usual. The choice is saved for when it ships. DisableAllKeychainLabel = Disable Credential Manager access DisableAllKeychainHelper = Not enforced on Windows yet: CodexBar still uses Credential Manager, DPAPI and browser cookies. The choice is saved for when blocking ships. CredentialAccessFooter = While Credential Manager access is disabled, the Claude keychain-prompt option stays locked. diff --git a/rust/src/locale/es-MX.ftl b/rust/src/locale/es-MX.ftl index 3826e47534..99a62fbe6e 100644 --- a/rust/src/locale/es-MX.ftl +++ b/rust/src/locale/es-MX.ftl @@ -634,7 +634,7 @@ CodexLocalLogsCaption = Agrega directorios home o sessions de Codex para estimac CodexLogPathsLabel = Rutas de logs de Codex CodexLogPathsHelper = Se acepta una raíz .codex; se escaneará la carpeta sessions dentro de ella. AvoidKeychainPromptsLabel = Evitar avisos de llavero (Claude) -AvoidKeychainPromptsHelper = Omitir lecturas de credenciales de llavero para Claude para evitar diálogos de permiso del SO. +AvoidKeychainPromptsHelper = Aún no se aplica en Windows: las credenciales de Claude se siguen leyendo como siempre. La opción se guarda para cuando esté disponible. DisableAllKeychainLabel = Deshabilitar el acceso al Administrador de credenciales DisableAllKeychainHelper = Aún no se aplica en Windows: CodexBar sigue usando el Administrador de credenciales, DPAPI y las cookies del navegador. La opción se guarda para cuando llegue el bloqueo. CredentialAccessFooter = Mientras el acceso al Administrador de credenciales esté deshabilitado, la opción de avisos del llavero de Claude queda bloqueada. diff --git a/rust/src/locale/ja-JP.ftl b/rust/src/locale/ja-JP.ftl index fc81af99cf..466ec1719c 100644 --- a/rust/src/locale/ja-JP.ftl +++ b/rust/src/locale/ja-JP.ftl @@ -589,7 +589,7 @@ CodexLocalLogsCaption = ローカルのコスト見積もりに使う Codex の CodexLogPathsLabel = Codex ログパス CodexLogPathsHelper = .codex ルートも指定できます。その中の sessions フォルダをスキャンします。 AvoidKeychainPromptsLabel = キーチェーン要求を出さない(Claude) -AvoidKeychainPromptsHelper = Claude のキーチェーン認証情報の読み取りをスキップし、OS の権限ダイアログを防ぐ。 +AvoidKeychainPromptsHelper = Windows ではまだ適用されません。Claude の資格情報は通常どおり読み取られます。この選択は機能の提供に備えて保存されます。 DisableAllKeychainLabel = 資格情報マネージャーへのアクセスを無効化 DisableAllKeychainHelper = Windows ではまだ適用されません。CodexBar は引き続き資格情報マネージャー、DPAPI、ブラウザーの Cookie を使います。この選択はブロック機能の提供に備えて保存されます。 CredentialAccessFooter = 資格情報マネージャーへのアクセスを無効にしている間、Claude のキーチェーンプロンプトのオプションはロックされます。 diff --git a/rust/src/locale/ko-KR.ftl b/rust/src/locale/ko-KR.ftl index 9270f6fa1d..37e6441055 100644 --- a/rust/src/locale/ko-KR.ftl +++ b/rust/src/locale/ko-KR.ftl @@ -634,7 +634,7 @@ CodexLocalLogsCaption = 로컬 비용 추정을 위해 Codex 홈 또는 sessions CodexLogPathsLabel = Codex 로그 경로 CodexLogPathsHelper = .codex 루트도 사용할 수 있으며, 그 안의 sessions 폴더를 스캔합니다. AvoidKeychainPromptsLabel = 키체인 창 표시 방지 (Claude) -AvoidKeychainPromptsHelper = OS 권한 대화 상자가 나타나지 않도록 Claude의 키체인 자격 증명 읽기를 건너뜁니다. +AvoidKeychainPromptsHelper = 아직 Windows에서 적용되지 않습니다. Claude 자격 증명은 평소처럼 읽습니다. 이 선택은 기능이 나올 때를 위해 저장됩니다. DisableAllKeychainLabel = 자격 증명 관리자 액세스 비활성화 DisableAllKeychainHelper = 아직 Windows에서 적용되지 않습니다. CodexBar는 계속 자격 증명 관리자, DPAPI 및 브라우저 쿠키를 사용합니다. 이 선택은 차단 기능이 나올 때를 위해 저장됩니다. CredentialAccessFooter = 자격 증명 관리자 액세스가 비활성화된 동안 Claude 키체인 프롬프트 옵션은 잠겨 있습니다. diff --git a/rust/src/locale/pt-BR.ftl b/rust/src/locale/pt-BR.ftl index 0ec4fef25e..edcf552df1 100644 --- a/rust/src/locale/pt-BR.ftl +++ b/rust/src/locale/pt-BR.ftl @@ -584,7 +584,7 @@ CodexLocalLogsCaption = Adicione diretórios iniciais ou de sessões do Codex pa CodexLogPathsLabel = Caminhos de log do Codex CodexLogPathsHelper = Uma raiz .codex é aceita; ele verificará a pasta de sessões dentro dela. AvoidKeychainPromptsLabel = Evite avisos de chaveiro (Claude) -AvoidKeychainPromptsHelper = Ignore as leituras de credenciais das chaves de Claude para evitar caixas de diálogo de permissão do sistema operacional. +AvoidKeychainPromptsHelper = Ainda não é aplicado no Windows: as credenciais do Claude continuam sendo lidas normalmente. A escolha fica salva para quando o recurso chegar. DisableAllKeychainLabel = Desativar o acesso ao Gerenciador de Credenciais DisableAllKeychainHelper = Ainda não é aplicado no Windows: o CodexBar continua usando o Gerenciador de Credenciais, a DPAPI e os cookies do navegador. A escolha fica salva para quando o bloqueio chegar. CredentialAccessFooter = Enquanto o acesso ao Gerenciador de Credenciais estiver desativado, a opção de avisos de chaves do Claude fica bloqueada. diff --git a/rust/src/locale/ru-RU.ftl b/rust/src/locale/ru-RU.ftl index a1f4ba55cb..a54c9e9033 100644 --- a/rust/src/locale/ru-RU.ftl +++ b/rust/src/locale/ru-RU.ftl @@ -616,7 +616,7 @@ CodexLocalLogsCaption = Добавьте домашний каталог Codex CodexLogPathsLabel = Пути журналов Codex CodexLogPathsHelper = Принимается корень .codex; он просканирует папку сеансов внутри него. AvoidKeychainPromptsLabel = Избегайте подсказок хранилища учетных данных (Claude) -AvoidKeychainPromptsHelper = Пропустить чтение учетных данных хранилища учетных данных для Claude, чтобы предотвратить диалоговые окна разрешений ОС. +AvoidKeychainPromptsHelper = Пока не применяется в Windows: учётные данные Claude по-прежнему читаются как обычно. Выбор сохраняется до появления функции. DisableAllKeychainLabel = Отключить доступ к диспетчеру учётных данных DisableAllKeychainHelper = Пока не применяется в Windows: CodexBar по-прежнему использует диспетчер учётных данных, DPAPI и файлы cookie браузера. Выбор сохраняется до появления блокировки. CredentialAccessFooter = Пока доступ к диспетчеру учётных данных отключён, параметр запросов связки ключей для Claude заблокирован. diff --git a/rust/src/locale/tests.rs b/rust/src/locale/tests.rs index 477d86b21f..722ffbdd20 100644 --- a/rust/src/locale/tests.rs +++ b/rust/src/locale/tests.rs @@ -629,3 +629,59 @@ fn resource_key_names(resource: &str) -> HashSet<&str> { .filter(|name| !name.is_empty()) .collect() } + +/// The Claude keychain-prompt option has no Windows consumer yet, so its helper +/// must say so instead of promising skipped credential reads. +#[test] +fn avoid_keychain_prompts_helper_says_it_is_not_enforced_on_windows() { + let expected = [ + ( + Language::English, + "Not enforced on Windows yet: Claude credentials are still read as usual. The choice is saved for when it ships.", + ), + ( + Language::Spanish, + "Aún no se aplica en Windows: las credenciales de Claude se siguen leyendo como siempre. La opción se guarda para cuando esté disponible.", + ), + ( + Language::Japanese, + "Windows ではまだ適用されません。Claude の資格情報は通常どおり読み取られます。この選択は機能の提供に備えて保存されます。", + ), + ( + Language::Korean, + "아직 Windows에서 적용되지 않습니다. Claude 자격 증명은 평소처럼 읽습니다. 이 선택은 기능이 나올 때를 위해 저장됩니다.", + ), + ( + Language::PortugueseBrazil, + "Ainda não é aplicado no Windows: as credenciais do Claude continuam sendo lidas normalmente. A escolha fica salva para quando o recurso chegar.", + ), + ( + Language::Russian, + "Пока не применяется в Windows: учётные данные Claude по-прежнему читаются как обычно. Выбор сохраняется до появления функции.", + ), + ( + Language::Turkish, + "Windows'ta henüz uygulanmıyor: Claude kimlik bilgileri her zamanki gibi okunur. Seçim, özellik geldiğinde kullanılmak üzere kaydedilir.", + ), + ( + Language::Ukrainian, + "Поки не застосовується в Windows: облікові дані Claude і далі зчитуються як звичайно. Вибір зберігається до появи функції.", + ), + ( + Language::Chinese, + "Windows 上尚未生效:仍会照常读取 Claude 凭据。此选项会保存,待该功能上线后使用。", + ), + ( + Language::ChineseTraditional, + "Windows 上尚未生效:仍會照常讀取 Claude 憑據。此選項會儲存,待該功能上線後使用。", + ), + ]; + assert_eq!(expected.len(), Language::all().len()); + for (language, text) in expected { + assert_eq!( + get_text(language, LocaleKey::AvoidKeychainPromptsHelper), + text, + "{language:?}" + ); + } +} diff --git a/rust/src/locale/tr-TR.ftl b/rust/src/locale/tr-TR.ftl index c85258c0c4..92be0e8470 100644 --- a/rust/src/locale/tr-TR.ftl +++ b/rust/src/locale/tr-TR.ftl @@ -616,7 +616,7 @@ CodexLocalLogsCaption = Yerel maliyet tahminleri için Codex ana klasörlerini v CodexLogPathsLabel = Codex günlük yolları CodexLogPathsHelper = Bir .codex kökü kabul edilir; içindeki sessions klasörü taranır. AvoidKeychainPromptsLabel = Anahtar zinciri istemlerinden kaçın (Claude) -AvoidKeychainPromptsHelper = İşletim sistemi izin iletişim kutularını önlemek için Claude anahtar zinciri kimlik bilgisi okumalarını atla. +AvoidKeychainPromptsHelper = Windows'ta henüz uygulanmıyor: Claude kimlik bilgileri her zamanki gibi okunur. Seçim, özellik geldiğinde kullanılmak üzere kaydedilir. DisableAllKeychainLabel = Kimlik Bilgileri Yöneticisi erişimini devre dışı bırak DisableAllKeychainHelper = Windows'ta henüz uygulanmıyor: CodexBar Kimlik Bilgileri Yöneticisi'ni, DPAPI'yi ve tarayıcı çerezlerini kullanmaya devam eder. Seçim, engelleme geldiğinde kullanılmak üzere kaydedilir. CredentialAccessFooter = Kimlik Bilgileri Yöneticisi erişimi kapalıyken Claude anahtar zinciri istemi seçeneği kilitli kalır. diff --git a/rust/src/locale/uk-UA.ftl b/rust/src/locale/uk-UA.ftl index 904c6f6f2d..b92f6f1529 100644 --- a/rust/src/locale/uk-UA.ftl +++ b/rust/src/locale/uk-UA.ftl @@ -616,7 +616,7 @@ CodexLocalLogsCaption = Додайте домашню папку Codex або п CodexLogPathsLabel = Шляхи до журналів Codex CodexLogPathsHelper = Можна вказати корінь .codex – тоді буде проскановано папку sessions усередині нього. AvoidKeychainPromptsLabel = Без запитів сховища облікових даних (Claude) -AvoidKeychainPromptsHelper = Не читати облікові дані Claude зі сховища, щоб ОС не показувала запити дозволу. +AvoidKeychainPromptsHelper = Поки не застосовується в Windows: облікові дані Claude і далі зчитуються як звичайно. Вибір зберігається до появи функції. DisableAllKeychainLabel = Вимкнути доступ до диспетчера облікових даних DisableAllKeychainHelper = Поки не застосовується в Windows: CodexBar і далі використовує диспетчер облікових даних, DPAPI та файли cookie браузера. Вибір зберігається до появи блокування. CredentialAccessFooter = Поки доступ до диспетчера облікових даних вимкнено, параметр запитів сховища ключів для Claude заблоковано. diff --git a/rust/src/locale/zh-CN.ftl b/rust/src/locale/zh-CN.ftl index 2be0c75380..77ffd2b8f3 100644 --- a/rust/src/locale/zh-CN.ftl +++ b/rust/src/locale/zh-CN.ftl @@ -589,7 +589,7 @@ CodexLocalLogsCaption = 添加 Codex 主目录或 sessions 目录,用于估算 CodexLogPathsLabel = Codex 日志路径 CodexLogPathsHelper = 可以填写 .codex 根目录;应用会扫描其中的 sessions 文件夹。 AvoidKeychainPromptsLabel = 避免钥匙串弹窗(Claude) -AvoidKeychainPromptsHelper = 跳过 Claude 的钥匙串凭据读取,避免系统权限弹窗。 +AvoidKeychainPromptsHelper = Windows 上尚未生效:仍会照常读取 Claude 凭据。此选项会保存,待该功能上线后使用。 DisableAllKeychainLabel = 禁用凭据管理器访问 DisableAllKeychainHelper = Windows 上尚未生效:CodexBar 仍会使用凭据管理器、DPAPI 和浏览器 Cookie。此选项会保存,待拦截功能上线后使用。 CredentialAccessFooter = 停用凭据管理器访问期间,Claude 钥匙串提示选项保持锁定。 diff --git a/rust/src/locale/zh-TW.ftl b/rust/src/locale/zh-TW.ftl index 7ca82e57f4..1737909c95 100644 --- a/rust/src/locale/zh-TW.ftl +++ b/rust/src/locale/zh-TW.ftl @@ -589,7 +589,7 @@ CodexLocalLogsCaption = 新增 Codex 主目錄或 sessions 目錄以估算本機 CodexLogPathsLabel = Codex 日誌路徑 CodexLogPathsHelper = 可以填寫 .codex 根目錄;應用程式會掃描其中的 sessions 資料夾。 AvoidKeychainPromptsLabel = 避免鑰匙串彈窗(Claude) -AvoidKeychainPromptsHelper = 跳過 Claude 的鑰匙串憑據讀取,避免系統許可權彈窗。 +AvoidKeychainPromptsHelper = Windows 上尚未生效:仍會照常讀取 Claude 憑據。此選項會儲存,待該功能上線後使用。 DisableAllKeychainLabel = 停用認證管理員存取 DisableAllKeychainHelper = Windows 上尚未生效:CodexBar 仍會使用認證管理員、DPAPI 和瀏覽器 Cookie。此選項會儲存,待封鎖功能上線後使用。 CredentialAccessFooter = 停用認證管理員存取期間,Claude 鑰匙串提示選項保持鎖定。 From 1152cab9831d8db18f1023c882449234ec783ff2 Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Sun, 11 Oct 2026 12:53:49 +0700 Subject: [PATCH 09/12] Show global shortcut register and clear errors instead of saving --- .../settings/tabs/GeneralTab.test.tsx | 40 +++++++++++++++++++ .../src/surfaces/settings/tabs/GeneralTab.tsx | 4 +- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.test.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.test.tsx index 30f640b935..e028f488ac 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.test.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.test.tsx @@ -519,6 +519,46 @@ describe("GeneralTab Mac grouped layout", () => { expect(screen.getByText("ShortcutRecordingHint")).toBeInTheDocument(); }); + it("shows the register error and does not save a shortcut that failed to register", async () => { + const set = vi.fn(); + render(); + vi.mocked(invoke).mockRejectedValueOnce("Failed to register shortcut \"Ctrl+Shift+K\": taken"); + + fireEvent.click(screen.getByRole("button", { name: "ShortcutRecordButton" })); + fireEvent.keyDown(window, { key: "k", code: "KeyK", ctrlKey: true, shiftKey: true }); + + expect(await screen.findByRole("alert")).toHaveTextContent( + "Failed to register shortcut \"Ctrl+Shift+K\": taken", + ); + expect(invoke).toHaveBeenCalledWith("register_global_shortcut", { accelerator: "Ctrl+Shift+K" }); + expect(set).not.toHaveBeenCalledWith({ globalShortcut: "Ctrl+Shift+K" }); + }); + + it("shows the unregister error and keeps the shortcut when clearing fails", async () => { + const set = vi.fn(); + render( + , + ); + vi.mocked(invoke).mockRejectedValueOnce(new Error("Failed to clear shortcuts: busy")); + + fireEvent.click(screen.getByRole("button", { name: "ShortcutClearButton" })); + + expect(await screen.findByRole("alert")).toHaveTextContent("Failed to clear shortcuts: busy"); + expect(invoke).toHaveBeenCalledWith("unregister_global_shortcut"); + expect(set).not.toHaveBeenCalledWith({ globalShortcut: "" }); + }); + + it("saves the shortcut after it registers", async () => { + const set = vi.fn(); + render(); + + fireEvent.click(screen.getByRole("button", { name: "ShortcutRecordButton" })); + fireEvent.keyDown(window, { key: "k", code: "KeyK", ctrlKey: true, shiftKey: true }); + + await waitFor(() => expect(set).toHaveBeenCalledWith({ globalShortcut: "Ctrl+Shift+K" })); + expect(screen.queryByRole("alert")).toBeNull(); + }); + it("quits from the footer button", () => { render(); fireEvent.click(screen.getByRole("button", { name: "QuitCodexBar" })); diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.tsx index 0488539def..3bc75c2ba7 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.tsx @@ -288,7 +288,7 @@ export default function GeneralTab({ async (accelerator: string) => { setShortcutError(null); try { - await registerGlobalShortcut(accelerator).catch(() => {}); + await registerGlobalShortcut(accelerator); set({ globalShortcut: accelerator }); } catch (err: unknown) { setShortcutError(err instanceof Error ? err.message : String(err)); @@ -300,7 +300,7 @@ export default function GeneralTab({ const clearShortcut = useCallback(async () => { setShortcutError(null); try { - await unregisterGlobalShortcut().catch(() => {}); + await unregisterGlobalShortcut(); set({ globalShortcut: "" }); } catch (err: unknown) { setShortcutError(err instanceof Error ? err.message : String(err)); From a8a07e7637daedcf17006d3b4cc539f66bf61499 Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Sun, 11 Oct 2026 12:53:49 +0700 Subject: [PATCH 10/12] Assert the rendered About build date --- .../src/surfaces/settings/tabs/AboutTab.test.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.test.tsx b/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.test.tsx index 3a930e1efe..8e1e518d53 100644 --- a/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.test.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/tabs/AboutTab.test.tsx @@ -16,7 +16,8 @@ const updateMocks = vi.hoisted(() => ({ vi.mock("../../../lib/tauri", () => tauriMocks); vi.mock("../../../hooks/useLocale", () => ({ - useLocale: () => ({ t: (key: string) => key }), + // AboutBuilt keeps its Fluent `{}` placeholder so the rendered date is asserted. + useLocale: () => ({ t: (key: string) => (key === "AboutBuilt" ? "Built {}" : key) }), })); vi.mock("../../../hooks/useUpdateState", () => ({ useUpdateState: () => ({ @@ -177,7 +178,7 @@ describe("AboutTab Mac grouped layout", () => { }); render(); - expect(await screen.findByText("AboutBuilt")).toBeInTheDocument(); + expect(await screen.findByText("Built 2026-10-11")).toBeInTheDocument(); expect(screen.getByText("Version 0.70.0 (7)")).toBeInTheDocument(); }); @@ -192,7 +193,7 @@ describe("AboutTab Mac grouped layout", () => { render(); await screen.findByText("Keep agent limits in view."); - expect(screen.queryByText("AboutBuilt")).toBeNull(); + expect(screen.queryByText(/^Built /)).toBeNull(); }); it("groups Updates then Links, with Submit an Issue as a link row", async () => { From 247cd09b4e5e1422364390108da6c8242aab8b8e Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Sun, 11 Oct 2026 12:53:49 +0700 Subject: [PATCH 11/12] Refresh the About build date when shell or core sources change --- apps/desktop-tauri/src-tauri/build.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/desktop-tauri/src-tauri/build.rs b/apps/desktop-tauri/src-tauri/build.rs index bb7145609f..7aaf4af1b1 100644 --- a/apps/desktop-tauri/src-tauri/build.rs +++ b/apps/desktop-tauri/src-tauri/build.rs @@ -7,8 +7,15 @@ fn main() { /// Expose the UTC build date as `CODEXBAR_BUILD_DATE` (YYYY-MM-DD) for the /// About pane. `SOURCE_DATE_EPOCH` pins it for reproducible builds. +/// +/// `tauri_build` registers its own rerun triggers, so Cargo would otherwise +/// keep this script's first date across later incremental builds. Rerunning +/// when the shell or `codexbar` sources change keeps the date current; those +/// changes recompile this crate anyway. fn emit_build_date() { println!("cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH"); + println!("cargo:rerun-if-changed=src"); + println!("cargo:rerun-if-changed=../../../rust/src"); let seconds = std::env::var("SOURCE_DATE_EPOCH") .ok() .and_then(|value| value.trim().parse::().ok()) From fd177862bbcda3f8b3354650b0e1a4db2d8e26de Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Sun, 11 Oct 2026 12:53:49 +0700 Subject: [PATCH 12/12] Say the Providers Claude keychain-prompt option is not enforced on Windows yet --- rust/src/locale/en-US.ftl | 2 +- rust/src/locale/es-MX.ftl | 2 +- rust/src/locale/ja-JP.ftl | 2 +- rust/src/locale/ko-KR.ftl | 2 +- rust/src/locale/pt-BR.ftl | 2 +- rust/src/locale/ru-RU.ftl | 2 +- rust/src/locale/tests.rs | 10 ++++++++-- rust/src/locale/tr-TR.ftl | 2 +- rust/src/locale/uk-UA.ftl | 2 +- rust/src/locale/zh-CN.ftl | 2 +- rust/src/locale/zh-TW.ftl | 2 +- 11 files changed, 18 insertions(+), 12 deletions(-) diff --git a/rust/src/locale/en-US.ftl b/rust/src/locale/en-US.ftl index 2fbb3dc93b..e2ed6da805 100644 --- a/rust/src/locale/en-US.ftl +++ b/rust/src/locale/en-US.ftl @@ -340,7 +340,7 @@ ProviderRegion = Region ProviderClaudeCookies = Claude cookies ProviderClaudeCookiesHelp = Browser cookies/sessionKey are preferred because they match Claude's settings usage page. ProviderClaudeAvoidKeychainPrompts = Avoid Keychain prompts -ProviderClaudeAvoidKeychainPromptsHelp = Use /usr/bin/security to read Claude credentials and avoid CodexBar keychain prompts. +ProviderClaudeAvoidKeychainPromptsHelp = Not enforced on Windows yet: Claude credentials are still read as usual. The choice is saved for when it ships. ProviderClaudeDailyRoutinesUsage = Show Daily Routines usage ProviderClaudeDailyRoutinesUsageHelp = Show the Daily Routines quota row for Claude web and OAuth usage. ProviderClaudeAllowReadingClaudeCodeCredentials = Allow reading Claude Code's credentials diff --git a/rust/src/locale/es-MX.ftl b/rust/src/locale/es-MX.ftl index 595eb28607..6e35219ae0 100644 --- a/rust/src/locale/es-MX.ftl +++ b/rust/src/locale/es-MX.ftl @@ -313,7 +313,7 @@ ProviderRegion = Región ProviderClaudeCookies = Cookies de Claude ProviderClaudeCookiesHelp = Se prefieren cookies/sessionKey del navegador porque coinciden con la página de uso de configuración de Claude. ProviderClaudeAvoidKeychainPrompts = Evitar avisos de llavero -ProviderClaudeAvoidKeychainPromptsHelp = Usar /usr/bin/security para leer credenciales de Claude y evitar avisos de llavero de CodexBar. +ProviderClaudeAvoidKeychainPromptsHelp = Aún no se aplica en Windows: las credenciales de Claude se siguen leyendo como siempre. La opción se guarda para cuando esté disponible. ProviderClaudeDailyRoutinesUsage = Mostrar uso de Daily Routines ProviderClaudeDailyRoutinesUsageHelp = Muestra la fila de cuota Daily Routines en el uso web/OAuth de Claude. ProviderClaudeAllowReadingClaudeCodeCredentials = Permitir leer las credenciales de Claude Code diff --git a/rust/src/locale/ja-JP.ftl b/rust/src/locale/ja-JP.ftl index e98f00fe39..432efc223c 100644 --- a/rust/src/locale/ja-JP.ftl +++ b/rust/src/locale/ja-JP.ftl @@ -313,7 +313,7 @@ ProviderRegion = リージョン ProviderClaudeCookies = Claude Cookie ProviderClaudeCookiesHelp = Claude の設定使用量ページに合致するため、ブラウザ Cookie / sessionKey が優先されます。 ProviderClaudeAvoidKeychainPrompts = キーチェーン要求を出さない -ProviderClaudeAvoidKeychainPromptsHelp = /usr/bin/security を使って Claude 認証情報を読み込み、CodexBar のキーチェーン要求を回避します。 +ProviderClaudeAvoidKeychainPromptsHelp = Windows ではまだ適用されません。Claude の資格情報は通常どおり読み取られます。この選択は機能の提供に備えて保存されます。 ProviderClaudeDailyRoutinesUsage = Daily Routines の使用量を表示 ProviderClaudeDailyRoutinesUsageHelp = Claude の Web/OAuth 使用量に Daily Routines 行を表示します。 ProviderClaudeAllowReadingClaudeCodeCredentials = Claude Code の認証情報の読み取りを許可 diff --git a/rust/src/locale/ko-KR.ftl b/rust/src/locale/ko-KR.ftl index 7755696769..3741416876 100644 --- a/rust/src/locale/ko-KR.ftl +++ b/rust/src/locale/ko-KR.ftl @@ -313,7 +313,7 @@ ProviderRegion = 지역 ProviderClaudeCookies = Claude 쿠키 ProviderClaudeCookiesHelp = 브라우저 쿠키/`sessionKey`가 Claude의 설정 사용량 페이지와 일치하므로 권장됩니다. ProviderClaudeAvoidKeychainPrompts = 키체인 메시지 표시 방지 -ProviderClaudeAvoidKeychainPromptsHelp = Claude 자격 증명을 읽을 때 `/usr/bin/security`를 사용하여 CodexBar 키체인 권한 창이 뜨지 않도록 합니다. +ProviderClaudeAvoidKeychainPromptsHelp = 아직 Windows에서 적용되지 않습니다. Claude 자격 증명은 평소처럼 읽습니다. 이 선택은 기능이 나올 때를 위해 저장됩니다. ProviderClaudeDailyRoutinesUsage = Daily Routines 사용량 표시 ProviderClaudeDailyRoutinesUsageHelp = Claude 웹/OAuth 사용량에 Daily Routines 할당량 행을 표시합니다. ProviderClaudeAllowReadingClaudeCodeCredentials = Claude Code 자격 증명 읽기 허용 diff --git a/rust/src/locale/pt-BR.ftl b/rust/src/locale/pt-BR.ftl index 5b775d8c6b..2f641ad1a6 100644 --- a/rust/src/locale/pt-BR.ftl +++ b/rust/src/locale/pt-BR.ftl @@ -333,7 +333,7 @@ ProviderRegion = Região ProviderClaudeCookies = Cookies do Claude ProviderClaudeCookiesHelp = Os cookies/sessionKey do navegador são preferidos porque correspondem à página de uso nas configurações do Claude. ProviderClaudeAvoidKeychainPrompts = Evitar solicitações do chaveiro -ProviderClaudeAvoidKeychainPromptsHelp = Usar /usr/bin/security para ler as credenciais do Claude e evitar solicitações do chaveiro do CodexBar. +ProviderClaudeAvoidKeychainPromptsHelp = Ainda não é aplicado no Windows: as credenciais do Claude continuam sendo lidas normalmente. A escolha fica salva para quando o recurso chegar. ProviderClaudeDailyRoutinesUsage = Mostrar uso de rotinas diárias ProviderClaudeDailyRoutinesUsageHelp = Mostrar a linha de cota de Rotinas Diárias para o uso Web e OAuth do Claude. ProviderClaudeAllowReadingClaudeCodeCredentials = Permitir a leitura das credenciais de Claude Code diff --git a/rust/src/locale/ru-RU.ftl b/rust/src/locale/ru-RU.ftl index af4048e3a7..377f7e636c 100644 --- a/rust/src/locale/ru-RU.ftl +++ b/rust/src/locale/ru-RU.ftl @@ -340,7 +340,7 @@ ProviderRegion = Регион ProviderClaudeCookies = Claude печенье ProviderClaudeCookiesHelp = Куки браузера/sessionKey являются предпочтительными, поскольку они соответствуют странице использования настроек Claude. ProviderClaudeAvoidKeychainPrompts = Избегайте подсказок хранилища учетных данных -ProviderClaudeAvoidKeychainPromptsHelp = Используйте /usr/bin/security для чтения учетных данных Claude и избегайте запросов цепочки ключей CodexBar. +ProviderClaudeAvoidKeychainPromptsHelp = Пока не применяется в Windows: учётные данные Claude по-прежнему читаются как обычно. Выбор сохраняется до появления функции. ProviderClaudeDailyRoutinesUsage = Показать использование ежедневных процедур ProviderClaudeDailyRoutinesUsageHelp = Отображение строки квоты ежедневных программ для использования Интернета Claude и OAuth. ProviderClaudeAllowReadingClaudeCodeCredentials = Разрешить чтение учетных данных Claude Code diff --git a/rust/src/locale/tests.rs b/rust/src/locale/tests.rs index 722ffbdd20..bfa66ae3b9 100644 --- a/rust/src/locale/tests.rs +++ b/rust/src/locale/tests.rs @@ -630,8 +630,9 @@ fn resource_key_names(resource: &str) -> HashSet<&str> { .collect() } -/// The Claude keychain-prompt option has no Windows consumer yet, so its helper -/// must say so instead of promising skipped credential reads. +/// The Claude keychain-prompt option has no Windows consumer yet, so both of its +/// helpers (Advanced pane and Providers > Claude) must say so instead of +/// promising skipped credential reads or a macOS `/usr/bin/security` path. #[test] fn avoid_keychain_prompts_helper_says_it_is_not_enforced_on_windows() { let expected = [ @@ -683,5 +684,10 @@ fn avoid_keychain_prompts_helper_says_it_is_not_enforced_on_windows() { text, "{language:?}" ); + assert_eq!( + get_text(language, LocaleKey::ProviderClaudeAvoidKeychainPromptsHelp), + text, + "{language:?} Providers > Claude" + ); } } diff --git a/rust/src/locale/tr-TR.ftl b/rust/src/locale/tr-TR.ftl index 3576096903..2c91234048 100644 --- a/rust/src/locale/tr-TR.ftl +++ b/rust/src/locale/tr-TR.ftl @@ -340,7 +340,7 @@ ProviderRegion = Bölge ProviderClaudeCookies = Claude çerezleri ProviderClaudeCookiesHelp = Claude'un ayarlar kullanım sayfasıyla eşleştikleri için tarayıcı çerezleri/sessionKey tercih edilir. ProviderClaudeAvoidKeychainPrompts = Anahtar Zinciri istemlerini atla -ProviderClaudeAvoidKeychainPromptsHelp = Claude kimlik bilgilerini okumak ve CodexBar Anahtar Zinciri istemlerini önlemek için /usr/bin/security kullanın. +ProviderClaudeAvoidKeychainPromptsHelp = Windows'ta henüz uygulanmıyor: Claude kimlik bilgileri her zamanki gibi okunur. Seçim, özellik geldiğinde kullanılmak üzere kaydedilir. ProviderClaudeDailyRoutinesUsage = Günlük rutinler kullanımını göster ProviderClaudeDailyRoutinesUsageHelp = Claude web ve OAuth kullanımı için Günlük Rutinler kota satırını göster. ProviderClaudeAllowReadingClaudeCodeCredentials = Claude Code kimlik bilgilerini okumaya izin ver diff --git a/rust/src/locale/uk-UA.ftl b/rust/src/locale/uk-UA.ftl index 4832cb93b0..35df104664 100644 --- a/rust/src/locale/uk-UA.ftl +++ b/rust/src/locale/uk-UA.ftl @@ -340,7 +340,7 @@ ProviderRegion = Регіон ProviderClaudeCookies = Файли cookie Claude ProviderClaudeCookiesHelp = Перевага за cookie/sessionKey з браузера, бо вони збігаються зі сторінкою використання в налаштуваннях Claude. ProviderClaudeAvoidKeychainPrompts = Без запитів сховища облікових даних -ProviderClaudeAvoidKeychainPromptsHelp = Читати облікові дані Claude через /usr/bin/security, щоб CodexBar не викликав запитів сховища облікових даних. +ProviderClaudeAvoidKeychainPromptsHelp = Поки не застосовується в Windows: облікові дані Claude і далі зчитуються як звичайно. Вибір зберігається до появи функції. ProviderClaudeDailyRoutinesUsage = Показувати використання Daily Routines ProviderClaudeDailyRoutinesUsageHelp = Показувати рядок квоти Daily Routines для використання Claude через веб і OAuth. ProviderClaudeAllowReadingClaudeCodeCredentials = Дозволити читати облікові дані Claude Code diff --git a/rust/src/locale/zh-CN.ftl b/rust/src/locale/zh-CN.ftl index 1aa58b46e2..fcfac1fd84 100644 --- a/rust/src/locale/zh-CN.ftl +++ b/rust/src/locale/zh-CN.ftl @@ -313,7 +313,7 @@ ProviderRegion = 地区 ProviderClaudeCookies = Claude Cookie ProviderClaudeCookiesHelp = 优先使用浏览器 Cookie/sessionKey,因为它与 Claude 设置页的用量一致。 ProviderClaudeAvoidKeychainPrompts = 避免钥匙串提示 -ProviderClaudeAvoidKeychainPromptsHelp = 使用 /usr/bin/security 读取 Claude 凭据,避免 CodexBar 的钥匙串提示。 +ProviderClaudeAvoidKeychainPromptsHelp = Windows 上尚未生效:仍会照常读取 Claude 凭据。此选项会保存,待该功能上线后使用。 ProviderClaudeDailyRoutinesUsage = 显示 Daily Routines 用量 ProviderClaudeDailyRoutinesUsageHelp = 显示 Claude 网页/OAuth 用量中的 Daily Routines 配额行。 ProviderClaudeAllowReadingClaudeCodeCredentials = 允许读取 Claude Code 的凭据 diff --git a/rust/src/locale/zh-TW.ftl b/rust/src/locale/zh-TW.ftl index e9a87fd263..25ab4ec7fe 100644 --- a/rust/src/locale/zh-TW.ftl +++ b/rust/src/locale/zh-TW.ftl @@ -313,7 +313,7 @@ ProviderRegion = 地區 ProviderClaudeCookies = Claude Cookie ProviderClaudeCookiesHelp = 優先使用瀏覽器 Cookie/sessionKey,因為它與 Claude 設定頁的用量一致。 ProviderClaudeAvoidKeychainPrompts = 避免鑰匙串提示 -ProviderClaudeAvoidKeychainPromptsHelp = 使用 /usr/bin/security 讀取 Claude 憑據,避免 CodexBar 的鑰匙串提示。 +ProviderClaudeAvoidKeychainPromptsHelp = Windows 上尚未生效:仍會照常讀取 Claude 憑據。此選項會儲存,待該功能上線後使用。 ProviderClaudeDailyRoutinesUsage = 顯示 Daily Routines 用量 ProviderClaudeDailyRoutinesUsageHelp = 顯示 Claude 網頁/OAuth 用量中的 Daily Routines 配額行。 ProviderClaudeAllowReadingClaudeCodeCredentials = 允許讀取 Claude Code 的憑證