Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ pub struct ProviderUsageSnapshot {
pub fetch_duration_ms: Option<u128>,
}

pub(crate) fn filter_hidden_codex_spark_rows(
snapshot: &mut ProviderUsageSnapshot,
spark_usage_visible: bool,
) {
if snapshot.provider_id == "codex" && !spark_usage_visible {
snapshot
.extra_rate_windows
.retain(|extra| !matches!(extra.id.as_str(), "codex-spark" | "codex-spark-weekly"));
}
}

pub(crate) fn pace_stage_str(stage: codexbar::core::PaceStage) -> &'static str {
use codexbar::core::PaceStage;
match stage {
Expand Down Expand Up @@ -420,6 +431,7 @@ pub struct SettingsSnapshot {
tray_scale_percent: u16,
powertoys_status_pipe_enabled: bool,
claude_avoid_keychain_prompts: bool,
codex_spark_usage_visible: bool,
disable_keychain_access: bool,
provider_metrics: std::collections::HashMap<String, &'static str>,
float_bar_enabled: bool,
Expand Down Expand Up @@ -456,6 +468,7 @@ pub fn get_settings_snapshot() -> SettingsSnapshot {
impl From<Settings> for SettingsSnapshot {
fn from(settings: Settings) -> Self {
let avoid_keychain_prompts = settings.claude_avoid_keychain_prompts();
let codex_spark_usage_visible = settings.codex_spark_usage_visible();

let provider_order = settings.provider_display_order_names();
let enabled_providers = provider_order
Expand Down Expand Up @@ -503,6 +516,7 @@ impl From<Settings> for SettingsSnapshot {
tray_scale_percent: settings.tray_scale_percent,
powertoys_status_pipe_enabled: settings.powertoys_status_pipe_enabled,
claude_avoid_keychain_prompts: avoid_keychain_prompts,
codex_spark_usage_visible,
disable_keychain_access: settings.disable_keychain_access,
provider_metrics,
float_bar_enabled: settings.float_bar_enabled,
Expand Down
35 changes: 20 additions & 15 deletions apps/desktop-tauri/src-tauri/src/commands/provider_detail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,25 +116,30 @@ pub fn get_provider_detail(
.iter()
.find(|s| s.provider_id == detail.id)
{
detail.email = snap.account_email.clone();
detail.plan = snap.plan_name.clone();
detail.organization = snap.account_organization.clone();
detail.source_label = if snap.source_label.is_empty() {
let mut snapshot = snap.clone();
super::filter_hidden_codex_spark_rows(
&mut snapshot,
Settings::load().codex_spark_usage_visible(),
);
detail.email = snapshot.account_email.clone();
detail.plan = snapshot.plan_name.clone();
detail.organization = snapshot.account_organization.clone();
detail.source_label = if snapshot.source_label.is_empty() {
None
} else {
Some(snap.source_label.clone())
Some(snapshot.source_label.clone())
};
detail.last_updated = Some(snap.updated_at.clone());
if snap.error.is_none() {
detail.session = Some(snap.primary.clone());
detail.weekly = snap.secondary.clone();
detail.model_specific = snap.model_specific.clone();
detail.tertiary = snap.tertiary.clone();
detail.extra_rate_windows = snap.extra_rate_windows.clone();
detail.cost = snap.cost.clone();
detail.pace = snap.pace.clone();
detail.last_updated = Some(snapshot.updated_at.clone());
if snapshot.error.is_none() {
detail.session = Some(snapshot.primary.clone());
detail.weekly = snapshot.secondary.clone();
detail.model_specific = snapshot.model_specific.clone();
detail.tertiary = snapshot.tertiary.clone();
detail.extra_rate_windows = snapshot.extra_rate_windows.clone();
detail.cost = snapshot.cost.clone();
detail.pace = snapshot.pace.clone();
}
detail.last_error = snap.error.clone();
detail.last_error = snapshot.error.clone();
detail.has_snapshot = true;
}

Expand Down
19 changes: 12 additions & 7 deletions apps/desktop-tauri/src-tauri/src/commands/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,14 +268,14 @@ async fn refresh_provider(app: tauri::AppHandle, id: ProviderId, ctx: FetchConte
let snapshot = fetch_provider_snapshot(id, ctx).await;

let state = app.state::<Mutex<AppState>>();
if let Ok(mut guard) = state.lock() {
let snapshot = if let Ok(mut guard) = state.lock() {
let snapshot = preserve_last_good_transient_failure(&mut guard, id, snapshot);
upsert_provider_cache(&mut guard.provider_cache, snapshot.clone());
drop(guard);
events::emit_provider_updated(&app, &snapshot);
snapshot
} else {
events::emit_provider_updated(&app, &snapshot);
}
snapshot
};
events::emit_provider_updated(&app, &snapshot);
}

pub(super) fn preserve_last_good_transient_failure(
Expand Down Expand Up @@ -441,8 +441,13 @@ pub async fn refresh_providers_if_stale(app: tauri::AppHandle) -> Result<(), Str
pub fn get_cached_providers(
state: tauri::State<'_, Mutex<AppState>>,
) -> Vec<ProviderUsageSnapshot> {
state
let mut snapshots = state
.lock()
.map(|guard| guard.provider_cache.clone())
.unwrap_or_default()
.unwrap_or_default();
let spark_usage_visible = Settings::load().codex_spark_usage_visible();
for snapshot in &mut snapshots {
super::filter_hidden_codex_spark_rows(snapshot, spark_usage_visible);
}
snapshots
}
6 changes: 5 additions & 1 deletion apps/desktop-tauri/src-tauri/src/commands/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct SettingsUpdate {
pub tray_scale_percent: Option<u16>,
pub powertoys_status_pipe_enabled: Option<bool>,
pub claude_avoid_keychain_prompts: Option<bool>,
pub codex_spark_usage_visible: Option<bool>,
pub disable_keychain_access: Option<bool>,
/// Map of provider CLI name → metric preference label.
pub provider_metrics: Option<std::collections::HashMap<String, String>>,
Expand Down Expand Up @@ -76,6 +77,7 @@ impl SettingsUpdate {
|| self.reset_time_relative.is_some()
|| self.menu_bar_display_mode.is_some()
|| self.provider_metrics.is_some()
|| self.codex_spark_usage_visible.is_some()
|| self.enabled_providers.is_some()
|| self.ui_language.is_some()
}
Expand Down Expand Up @@ -219,6 +221,9 @@ impl SettingsUpdate {
if let Some(v) = self.claude_avoid_keychain_prompts {
settings.set_claude_avoid_keychain_prompts(v);
}
if let Some(v) = self.codex_spark_usage_visible {
settings.set_codex_spark_usage_visible(v);
}
if let Some(v) = self.disable_keychain_access {
settings.disable_keychain_access = v;
if v {
Expand Down Expand Up @@ -334,7 +339,6 @@ pub async fn update_settings(
if refresh_tray_presentation {
crate::tray_bridge::refresh_tray_presentation(&app);
}

// Notify other windows (PopOut dashboard, tray, float bar) so they re-read
// settings live — e.g. the Display tab's window-scale slider takes effect
// immediately instead of only after the PopOut is reopened.
Expand Down
33 changes: 31 additions & 2 deletions apps/desktop-tauri/src-tauri/src/commands/tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::collections::HashMap;

use super::{
ProviderSummary, ProviderUsageSnapshot, provider_cookie_source_lookup, provider_region_lookup,
validate_external_url, validate_surface_target,
NamedRateWindowSnapshot, ProviderSummary, ProviderUsageSnapshot, provider_cookie_source_lookup,
provider_region_lookup, validate_external_url, validate_surface_target,
};
use crate::surface::SurfaceMode;
use crate::surface_target::SurfaceTarget;
Expand Down Expand Up @@ -695,6 +695,35 @@ fn provider_cache_upsert_replaces_existing_provider() {
assert_eq!(cache[0].error.as_deref(), Some("new"));
}

#[test]
fn hiding_codex_spark_rows_preserves_other_extra_usage() {
let metadata = instantiate_provider(ProviderId::Codex).metadata().clone();
let result = ProviderFetchResult {
usage: codexbar::core::UsageSnapshot::new(codexbar::core::RateWindow::new(10.0)),
cost: None,
source_label: "CLI".to_string(),
};
let mut snapshot =
ProviderUsageSnapshot::from_fetch_result(ProviderId::Codex, &metadata, &result);
snapshot.extra_rate_windows = vec![
NamedRateWindowSnapshot {
id: "codex-spark".to_string(),
title: "Codex Spark 5-hour".to_string(),
window: snapshot.primary.clone(),
},
NamedRateWindowSnapshot {
id: "credits".to_string(),
title: "Credits".to_string(),
window: snapshot.primary.clone(),
},
];

super::filter_hidden_codex_spark_rows(&mut snapshot, false);

assert_eq!(snapshot.extra_rate_windows.len(), 1);
assert_eq!(snapshot.extra_rate_windows[0].id, "credits");
}

#[test]
fn claude_transient_auth_failure_preserves_first_last_good_snapshot() {
let metadata = instantiate_provider(ProviderId::Claude).metadata().clone();
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop-tauri/src-tauri/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ pub fn emit_surface_mode_changed(
}

pub fn emit_provider_updated(app: &AppHandle, snapshot: &ProviderUsageSnapshot) {
let mut snapshot = snapshot.clone();
crate::commands::filter_hidden_codex_spark_rows(
&mut snapshot,
codexbar::settings::Settings::load().codex_spark_usage_visible(),
);
let _ = app.emit(PROVIDER_UPDATED, snapshot);
}

Expand Down
24 changes: 19 additions & 5 deletions apps/desktop-tauri/src-tauri/src/tray_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,9 @@ pub(crate) fn rebuild_tray_menu(app: &AppHandle) {
let settings = Settings::load();
let status_labels = if let Some(st) = app.try_state::<Mutex<AppState>>() {
let guard = st.lock().unwrap();
status_labels_for_settings(&settings, &guard.provider_cache, settings.ui_language)
let snapshots =
presentation_snapshots(&guard.provider_cache, settings.codex_spark_usage_visible());
status_labels_for_settings(&settings, &snapshots, settings.ui_language)
} else {
vec![]
};
Expand All @@ -388,7 +390,8 @@ pub fn update_tray_status_items(
) {
let catalog = crate::commands::get_provider_catalog();
let settings = Settings::load();
let status_labels = status_labels_for_settings(&settings, snapshots, settings.ui_language);
let snapshots = presentation_snapshots(snapshots, settings.codex_spark_usage_visible());
let status_labels = status_labels_for_settings(&settings, &snapshots, settings.ui_language);

if let Ok(menu) = build_native_tray_menu(app, &catalog, &status_labels)
&& let Some(tray) = app.tray_by_id("codexbar-main")
Expand All @@ -403,11 +406,21 @@ pub(crate) fn refresh_tray_presentation(app: &AppHandle) {
.try_state::<Mutex<AppState>>()
.map(|st| st.lock().unwrap().provider_cache.clone())
.unwrap_or_default();

update_tray_status_items(app, &snapshots);
update_tray_icon_and_tooltip(app, &snapshots);
}

fn presentation_snapshots(
snapshots: &[crate::commands::ProviderUsageSnapshot],
spark_usage_visible: bool,
) -> Vec<crate::commands::ProviderUsageSnapshot> {
let mut snapshots = snapshots.to_vec();
for snapshot in &mut snapshots {
crate::commands::filter_hidden_codex_spark_rows(snapshot, spark_usage_visible);
}
snapshots
}

/// Update the tray icon pixels and tooltip text to reflect current provider usage.
///
/// Behaviour mirrors egui's `choose_tray_update_plan` (rust/src/native_ui/app.rs):
Expand All @@ -429,7 +442,8 @@ pub fn update_tray_icon_and_tooltip(

// ── Icon ─────────────────────────────────────────────────────────────
let settings = Settings::load();
let ordered_snapshots = ordered_snapshot_refs(&settings, snapshots);
let snapshots = presentation_snapshots(snapshots, settings.codex_spark_usage_visible());
let ordered_snapshots = ordered_snapshot_refs(&settings, &snapshots);
let ok_snapshots: Vec<_> = ordered_snapshots
.iter()
.copied()
Expand Down Expand Up @@ -458,7 +472,7 @@ pub fn update_tray_icon_and_tooltip(
let _ = tray.set_icon(Some(icon));

// ── Tooltip ───────────────────────────────────────────────────────────
let tooltip = build_tooltip(snapshots, settings.ui_language);
let tooltip = build_tooltip(&snapshots, settings.ui_language);
let _ = tray.set_tooltip(Some(tooltip));
}

Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ function settings(overrides: Partial<SettingsSnapshot> = {}): SettingsSnapshot {
trayScalePercent: 100,
powertoysStatusPipeEnabled: false,
claudeAvoidKeychainPrompts: false,
codexSparkUsageVisible: true,
disableKeychainAccess: false,
providerMetrics: {},
floatBarEnabled: false,
Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src/floatbar/FloatBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ function settings(overrides: Partial<SettingsSnapshot> = {}): SettingsSnapshot {
trayScalePercent: 100,
powertoysStatusPipeEnabled: false,
claudeAvoidKeychainPrompts: false,
codexSparkUsageVisible: true,
disableKeychainAccess: false,
providerMetrics: {},
floatBarEnabled: true,
Expand Down
Loading