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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

## [Windows] Unreleased

- Tray: add a **Progress bars stacked** menu bar style that draws the session and weekly meters as two equal-height rows on the single tray icon, with the same windows and pace tint as **Meter bars**; older settings files keep their style (upstream #4436).
- Settings providers: show only enabled providers from the sidebar options menu, search by CLI alias (such as `openai` for Codex), restore the default provider order, and undo a drag reorder or a restore with Ctrl+Z or the options menu. Hidden providers keep their slots when the order changes (upstream #4440).
- Add **Color by provider** to Settings > Menu Bar (off by default): tints the tray usage bars and percent text with the shown provider's brand or accent color, keeps pace colors and the error colors unchanged, and falls back to the usual colors when a tint would be hard to see on the tray background (upstream #4321).
- OpenAI API: each Admin refresh also reads the last hour of 1-minute usage from the Admin Usage API (input plus output tokens, cached input included, Batch API traffic kept out of limit headroom) and carries a Tokens/min grid with a reporting frontier, so buckets that arrive late show as pending instead of zero. The backend can also load 15-minute, 1-hour or 6-hour windows, read per-project rate-limit caps, and measure headroom against manual per-model organization limits stored in settings. The per-refresh request can be turned off, and a failure never affects the daily usage. The chart, menu bar token and Settings controls come in a later release (upstream #4495).
Expand Down
11 changes: 11 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,17 @@ mod tests {
codexbar::settings::MenuBarIconStyle::Bars
);
assert!(!settings.menu_bar_shows_percent);

// Progress bars stacked is not the percent style, so the legacy flag stays off.
let update: SettingsUpdate =
serde_json::from_str(r#"{"menuBarIconStyle":"stackedBars"}"#).unwrap();
assert!(update.refreshes_tray_presentation());
update.apply_display_settings(&mut settings);
assert_eq!(
settings.menu_bar_icon_style,
codexbar::settings::MenuBarIconStyle::StackedBars
);
assert!(!settings.menu_bar_shows_percent);
}

#[test]
Expand Down
43 changes: 35 additions & 8 deletions apps/desktop-tauri/src-tauri/src/tray_presentation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use codexbar::core::{IconLane, ProviderId, UsagePace};
use codexbar::settings::{Language, MetricPreference, Settings, TrayIconMode};
use codexbar::tray::{
TrayPaceColor, render_bar_icon_rgba_tinted, render_percent_icon_rgba_tinted,
render_stacked_bar_icon_rgba_tinted,
render_stacked_bar_icon_rgba_tinted, render_stacked_bars_icon_rgba_tinted,
};

/// Opt-in provider accent for a tray meter (`menu_bar_color_by_provider`).
Expand All @@ -20,6 +20,13 @@ enum TrayIconPlan {
has_error: bool,
provider_tint: Option<ProviderRgb>,
},
/// The same session and weekly windows as `Bars`, drawn as equal rows.
StackedBars {
primary_percent: f64,
secondary_percent: Option<f64>,
has_error: bool,
provider_tint: Option<ProviderRgb>,
},
Percent {
percent: f64,
has_error: bool,
Expand Down Expand Up @@ -183,7 +190,9 @@ impl<'a> TrayPresentationPlan<'a> {

let icon_pace = match icon {
TrayIconPlan::Stacked { .. } => None,
TrayIconPlan::Bars { .. } | TrayIconPlan::Percent { .. } => icon_pace,
TrayIconPlan::Bars { .. }
| TrayIconPlan::StackedBars { .. }
| TrayIconPlan::Percent { .. } => icon_pace,
};

Self {
Expand Down Expand Up @@ -214,6 +223,18 @@ impl<'a> TrayPresentationPlan<'a> {
has_error,
provider_tint,
} => render_percent_icon_rgba_tinted(percent, has_error, self.icon_pace, provider_tint),
TrayIconPlan::StackedBars {
primary_percent,
secondary_percent,
has_error,
provider_tint,
} => render_stacked_bars_icon_rgba_tinted(
primary_percent,
secondary_percent,
has_error,
self.icon_pace,
provider_tint,
),
TrayIconPlan::Stacked {
top_percent,
bottom_percent,
Expand Down Expand Up @@ -253,19 +274,25 @@ fn resolve_single_provider_icon_plan(
has_error: bool,
provider_tint: Option<ProviderRgb>,
) -> TrayIconPlan {
if settings.menu_bar_icon_style == codexbar::settings::MenuBarIconStyle::Percent {
TrayIconPlan::Percent {
use codexbar::settings::MenuBarIconStyle;
match settings.menu_bar_icon_style {
MenuBarIconStyle::Percent => TrayIconPlan::Percent {
percent: primary_percent,
has_error,
provider_tint,
}
} else {
TrayIconPlan::Bars {
},
MenuBarIconStyle::StackedBars => TrayIconPlan::StackedBars {
primary_percent,
secondary_percent,
has_error,
provider_tint,
}
},
MenuBarIconStyle::Bars => TrayIconPlan::Bars {
primary_percent,
secondary_percent,
has_error,
provider_tint,
},
}
}

Expand Down
143 changes: 143 additions & 0 deletions apps/desktop-tauri/src-tauri/src/tray_presentation_tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::*;
use codexbar::core::{ProviderId, ProviderStateKind};
use codexbar::tray::render_stacked_bars_icon_rgba_with_pace;

fn fake_snapshot(id: &str, display_name: &str, used_percent: f64) -> ProviderUsageSnapshot {
fake_snapshot_with(id, display_name, used_percent, None, None, None)
Expand Down Expand Up @@ -1001,6 +1002,118 @@ fn retired_minimal_display_mode_no_longer_forces_most_used_provider() {
assert_eq!(plan.status_rows[0].snapshot.provider_id, "codex");
}

#[test]
fn stacked_bars_style_plans_the_same_windows_as_bars_as_equal_rows() {
let snapshots = vec![fake_snapshot_with(
"codex",
"Codex",
30.0,
Some(65.0),
None,
None,
)];
let bars_settings = Settings {
menu_bar_icon_style: codexbar::settings::MenuBarIconStyle::Bars,
show_as_used: true,
..Settings::default()
};
let stacked_settings = Settings {
menu_bar_icon_style: codexbar::settings::MenuBarIconStyle::StackedBars,
show_as_used: true,
..Settings::default()
};

let TrayIconPlan::Bars {
primary_percent,
secondary_percent,
has_error,
provider_tint,
} = TrayPresentationPlan::resolve(&bars_settings, &snapshots, None).icon
else {
panic!("bars style should plan Bars");
};
let stacked = TrayPresentationPlan::resolve(&stacked_settings, &snapshots, None);
assert_eq!(
stacked.icon,
TrayIconPlan::StackedBars {
primary_percent,
secondary_percent,
has_error,
provider_tint,
}
);
assert_eq!(
stacked.render_icon(),
render_stacked_bars_icon_rgba_with_pace(
primary_percent,
secondary_percent,
has_error,
None
)
);
assert_ne!(
stacked.render_icon(),
TrayPresentationPlan::resolve(&bars_settings, &snapshots, None).render_icon()
);
}

#[test]
fn stacked_bars_style_with_one_window_draws_the_bars_fallback() {
let snapshots = vec![fake_snapshot("codex", "Codex", 30.0)];
let bars_settings = Settings {
menu_bar_icon_style: codexbar::settings::MenuBarIconStyle::Bars,
show_as_used: true,
..Settings::default()
};
let stacked_settings = Settings {
menu_bar_icon_style: codexbar::settings::MenuBarIconStyle::StackedBars,
..bars_settings.clone()
};
let bars = TrayPresentationPlan::resolve(&bars_settings, &snapshots, None);
let stacked = TrayPresentationPlan::resolve(&stacked_settings, &snapshots, None);
assert!(matches!(
stacked.icon,
TrayIconPlan::StackedBars {
secondary_percent: None,
..
}
));
assert_eq!(stacked.render_icon(), bars.render_icon());
}

#[test]
fn stacked_bars_style_keeps_the_pace_tint_on_both_rows() {
let settings = Settings {
menu_bar_icon_style: codexbar::settings::MenuBarIconStyle::StackedBars,
..tint_settings()
};
let snapshots = vec![paced_snapshot(
"synthetic",
paced_window(70.0, Some(300), 150),
)];
let plan = TrayPresentationPlan::resolve_at(&settings, &snapshots, None, pace_now());
assert_eq!(plan.icon_pace, Some(TrayPaceColor::Ahead));

let TrayIconPlan::StackedBars {
primary_percent,
secondary_percent,
has_error,
provider_tint: None,
} = plan.icon
else {
panic!("stacked bars style should plan StackedBars");
};
assert_eq!(
plan.render_icon(),
render_stacked_bars_icon_rgba_with_pace(
primary_percent,
secondary_percent,
has_error,
Some(TrayPaceColor::Ahead),
)
);
}

const CODEX_BRAND: (u8, u8, u8) = (0x49, 0xA3, 0xB0);
const CLAUDE_BRAND: (u8, u8, u8) = (0xCC, 0x7C, 0x5E);

Expand Down Expand Up @@ -1168,3 +1281,33 @@ fn parse_hex_rgb_accepts_only_six_digit_hex() {
assert_eq!(parse_hex_rgb("#GG0000"), None);
assert_eq!(parse_hex_rgb(""), None);
}

#[test]
fn color_by_provider_tints_both_stacked_bars_rows() {
let snapshots = vec![fake_snapshot_with(
"codex",
"Codex",
30.0,
Some(65.0),
None,
None,
)];
let settings = color_by_provider_settings(codexbar::settings::MenuBarIconStyle::StackedBars);
let plan = TrayPresentationPlan::resolve(&settings, &snapshots, None);
assert!(matches!(
plan.icon,
TrayIconPlan::StackedBars {
provider_tint: Some(CODEX_BRAND),
..
}
));
let (rgba, _, _) = plan.render_icon();
assert_eq!(
pixel_at(&rgba, 8, 8),
[CODEX_BRAND.0, CODEX_BRAND.1, CODEX_BRAND.2, 255]
);
assert_eq!(
pixel_at(&rgba, 8, 20),
[CODEX_BRAND.0, CODEX_BRAND.1, CODEX_BRAND.2, 255]
);
}
1 change: 1 addition & 0 deletions apps/desktop-tauri/src/i18n/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1603,6 +1603,7 @@ export const ALL_LOCALE_KEYS = [
"MenuBarIconStyleHelper",
"MenuBarIconStyleBars",
"MenuBarIconStylePercent",
"MenuBarIconStyleStackedBars",
"CombinedIconSectionTitle",
"CombinedIconStyleLabel",
"CombinedIconStyleSwitcher",
Expand Down
18 changes: 18 additions & 0 deletions apps/desktop-tauri/src/surfaces/settings/tabs/MenuBarTab.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,24 @@ describe("MenuBarTab Icon section", () => {
expect(set).toHaveBeenCalledWith({ menuBarIconStyle: "percent" });
});

it("offers Progress bars stacked after the other menu bar styles and saves it", () => {
const { set } = renderTab();
const options = Array.from(select("MenuBarIconStyleLabel").options).map((o) => [o.value, o.textContent]);
expect(options).toEqual([
["bars", "MenuBarIconStyleBars"],
["percent", "MenuBarIconStylePercent"],
["stackedBars", "MenuBarIconStyleStackedBars"],
]);
fireEvent.change(select("MenuBarIconStyleLabel"), { target: { value: "stackedBars" } });
expect(set).toHaveBeenCalledWith({ menuBarIconStyle: "stackedBars" });
});

it("shows a saved Progress bars stacked style", () => {
renderTab({ menuBarIconStyle: "stackedBars" });
expect(select("MenuBarIconStyleLabel").value).toBe("stackedBars");
expect(select("MenuBarIconStyleLabel").disabled).toBe(false);
});

it("disables the Menu bar style while the icon is stacked", () => {
renderTab({ trayIconMode: "stacked" });
expect(select("MenuBarIconStyleLabel").disabled).toBe(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export default function MenuBarTab({
options={[
{ value: "bars", label: t("MenuBarIconStyleBars") },
{ value: "percent", label: t("MenuBarIconStylePercent") },
{ value: "stackedBars", label: t("MenuBarIconStyleStackedBars") },
]}
onChange={(v) => set({ menuBarIconStyle: v as MenuBarIconStyle })}
/>
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop-tauri/src/types/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export type OverviewLayout = "detailed" | "compact";
/** How cost is rendered on provider MenuCards (#2976). */
export type CostSummaryDisplayStyle = "compact" | "detailed" | "hidden";
/** Settings > Menu Bar > Menu bar style (`menu_bar_icon_style`). */
export type MenuBarIconStyle = "bars" | "percent";
export type MenuBarIconStyle = "bars" | "percent" | "stackedBars";
/** Settings > Menu > Workday tick appearance (`workday_tick_style`). */
export type WorkdayTickStyle = "hidden" | "subtle" | "high_contrast";
export type FloatBarOrientation = "horizontal" | "vertical";
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1892,6 +1892,7 @@ locale_keys! {
MenuBarIconStyleHelper,
MenuBarIconStyleBars,
MenuBarIconStylePercent,
MenuBarIconStyleStackedBars,
CombinedIconSectionTitle,
CombinedIconStyleLabel,
CombinedIconStyleSwitcher,
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1562,6 +1562,7 @@ MenuBarIconStyleLabel = Menu bar style
MenuBarIconStyleHelper = How the tray icon is drawn.
MenuBarIconStyleBars = Meter bars
MenuBarIconStylePercent = Icon & percent
MenuBarIconStyleStackedBars = Progress bars stacked
CombinedIconSectionTitle = Combined icon
CombinedIconStyleLabel = Combined icon style
CombinedIconStyleSwitcher = Switcher
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/es-MX.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,7 @@ MenuBarIconStyleLabel = Estilo de la barra de menú
MenuBarIconStyleHelper = Cómo se dibuja el icono de la bandeja.
MenuBarIconStyleBars = Barras de medidor
MenuBarIconStylePercent = Icono y porcentaje
MenuBarIconStyleStackedBars = Barras de progreso apiladas
CombinedIconSectionTitle = Icono combinado
CombinedIconStyleLabel = Estilo del icono combinado
CombinedIconStyleSwitcher = Selector
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/ja-JP.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,7 @@ MenuBarIconStyleLabel = メニューバーのスタイル
MenuBarIconStyleHelper = トレイアイコンの描画方法。
MenuBarIconStyleBars = メーターバー
MenuBarIconStylePercent = アイコンとパーセント
MenuBarIconStyleStackedBars = 進度バーのスタック
CombinedIconSectionTitle = 統合アイコン
CombinedIconStyleLabel = 統合アイコンのスタイル
CombinedIconStyleSwitcher = 切り替え
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/ko-KR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,7 @@ MenuBarIconStyleLabel = 메뉴 바 스타일
MenuBarIconStyleHelper = 트레이 아이콘을 그리는 방식입니다.
MenuBarIconStyleBars = 미터 막대
MenuBarIconStylePercent = 아이콘 및 퍼센트
MenuBarIconStyleStackedBars = 진행률 막대 세로로 쌓기
CombinedIconSectionTitle = 통합 아이콘
CombinedIconStyleLabel = 통합 아이콘 스타일
CombinedIconStyleSwitcher = 전환기
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/pt-BR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,7 @@ MenuBarIconStyleLabel = Estilo da barra de menus
MenuBarIconStyleHelper = Como o ícone da bandeja é desenhado.
MenuBarIconStyleBars = Barras de medidor
MenuBarIconStylePercent = Ícone e porcentagem
MenuBarIconStyleStackedBars = Barras de progresso empilhadas
CombinedIconSectionTitle = Ícone combinado
CombinedIconStyleLabel = Estilo do ícone combinado
CombinedIconStyleSwitcher = Alternador
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/ru-RU.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,7 @@ MenuBarIconStyleLabel = Стиль значка
MenuBarIconStyleHelper = Как отображается значок в трее.
MenuBarIconStyleBars = Шкалы
MenuBarIconStylePercent = Значок и процент
MenuBarIconStyleStackedBars = Полосы прогресса друг над другом
CombinedIconSectionTitle = Общий значок
CombinedIconStyleLabel = Стиль общего значка
CombinedIconStyleSwitcher = Переключатель
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/tr-TR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,7 @@ MenuBarIconStyleLabel = Menü çubuğu stili
MenuBarIconStyleHelper = Tepsi simgesinin nasıl çizileceği.
MenuBarIconStyleBars = Ölçüm çubukları
MenuBarIconStylePercent = Simge ve yüzde
MenuBarIconStyleStackedBars = İstiflenmiş ilerleme çubukları
CombinedIconSectionTitle = Birleşik simge
CombinedIconStyleLabel = Birleşik simge stili
CombinedIconStyleSwitcher = Değiştirici
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/uk-UA.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,7 @@ MenuBarIconStyleLabel = Стиль значка
MenuBarIconStyleHelper = Як відображається значок у треї.
MenuBarIconStyleBars = Шкали
MenuBarIconStylePercent = Значок і відсоток
MenuBarIconStyleStackedBars = Індикатори прогресу штабелем
CombinedIconSectionTitle = Спільний значок
CombinedIconStyleLabel = Стиль спільного значка
CombinedIconStyleSwitcher = Перемикач
Expand Down
Loading