Skip to content
Closed
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 apps/desktop-tauri/src-tauri/src/auto_resume_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
}
}

Expand Down
6 changes: 6 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ pub struct RateWindowSnapshot {
pub is_exhausted: bool,
#[serde(default)]
pub is_informational: bool,
/// `reset_description` is a detail line (for example spend amounts), not reset wording.
#[serde(default)]
pub description_is_detail: bool,
#[serde(default)]
pub reserve_percent: Option<f64>,
#[serde(default)]
Expand Down Expand Up @@ -53,6 +56,7 @@ impl RateWindowSnapshot {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: rw.description_is_detail,
}
}

Expand Down Expand Up @@ -518,6 +522,7 @@ impl ProviderUsageSnapshot {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
},
primary_label: Some(metadata.session_label.to_string()),
secondary: None,
Expand Down Expand Up @@ -980,6 +985,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
}
}

Expand Down
45 changes: 45 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/bridge/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ fn compact_reset_description(
return Some(format_compact_reset_countdown(dt, lang));
}

if window.description_is_detail {
return None;
}

window
.reset_description
.as_deref()
Expand Down Expand Up @@ -128,3 +132,44 @@ pub(crate) fn friendly_provider_error(id: ProviderId, error: &str) -> String {

trimmed.to_string()
}

#[cfg(test)]
mod tests {
use super::*;
use codexbar::core::RateWindow;
use codexbar::settings::Language;

#[test]
fn detail_backed_description_is_never_a_tray_reset_label() {
let window = RateWindowSnapshot::from_rate_window(
&RateWindow::with_details(
13.0,
None,
None,
Some("34.07 EUR / 255.00 EUR · 220.93 EUR remaining".to_string()),
)
.with_description_as_detail(),
);

assert!(window.description_is_detail);
assert_eq!(compact_tray_status_label(&window, Language::English), "13%");
}

#[test]
fn detail_backed_window_keeps_its_countdown_when_reset_is_known() {
let window = RateWindowSnapshot::from_rate_window(
&RateWindow::with_details(
13.0,
None,
Some(chrono::Utc::now() + chrono::Duration::minutes(125)),
Some("34.07 EUR / 255.00 EUR · 220.93 EUR remaining".to_string()),
)
.with_description_as_detail(),
);

let label = compact_tray_status_label(&window, Language::English);

assert!(label.starts_with("13% • "), "{label}");
assert!(!label.contains("EUR"), "{label}");
}
}
1 change: 1 addition & 0 deletions apps/desktop-tauri/src-tauri/src/commands/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,7 @@ mod reset_backfill_tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
}
}

Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src-tauri/src/powertoys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ mod tests {
reserve_percent: None,
reserve_description: None,
reserve_eta_seconds: None,
description_is_detail: false,
reserve_will_last_to_reset: false,
}
}
Expand Down
6 changes: 6 additions & 0 deletions apps/desktop-tauri/src-tauri/src/tray_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
},
primary_label: None,
secondary: secondary_percent.map(|pct| crate::commands::RateWindowSnapshot {
Expand All @@ -1048,6 +1049,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
}),
secondary_label: None,
model_specific: None,
Expand All @@ -1063,6 +1065,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
}),
tertiary_label: None,
extra_rate_windows: Vec::new(),
Expand Down Expand Up @@ -1127,6 +1130,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
},
}
}
Expand Down Expand Up @@ -1537,6 +1541,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
});

let (primary, _) = selected_tray_percents(&snapshot, &settings);
Expand Down Expand Up @@ -1581,6 +1586,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
});
snapshot.extra_rate_windows.push(fake_extra_window(90.0));

Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-tauri/src-tauri/src/usage_metric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ fn same_window(left: &RateWindowSnapshot, right: &RateWindowSnapshot) -> bool {
&& left.window_minutes == right.window_minutes
&& left.resets_at == right.resets_at
&& left.reset_description == right.reset_description
&& left.description_is_detail == right.description_is_detail
&& left.is_informational == right.is_informational
}

Expand Down Expand Up @@ -267,6 +268,7 @@ fn derived_window(used_percent: f64, resets_at: Option<String>) -> RateWindowSna
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
description_is_detail: false,
}
}

Expand Down
42 changes: 41 additions & 1 deletion apps/desktop-tauri/src/components/MenuCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function rateWindow(
function provider(
error: string | null,
usedPercent = 0,
opts: { exhausted?: boolean; resetDescription?: string | null } = {},
opts: { exhausted?: boolean; resetDescription?: string | null; resetsAt?: string | null } = {},
): ProviderUsageSnapshot {
return {
providerId: "claude",
Expand Down Expand Up @@ -356,6 +356,46 @@ describe("MenuCard", () => {
expect(await screen.findByText("Fable only")).toBeInTheDocument();
});

it.each([false, true])(
"shows detail-backed amounts as their own line, never as reset text (resetsAt: %s)",
async (hasReset) => {
const resetsAt = hasReset
? new Date(Date.now() + 3 * 60 * 60 * 1000 + 30_000).toISOString()
: null;
const snapshot = provider(null, 75, {
resetDescription: "19.17 EUR / 25.50 EUR · 6.33 EUR remaining",
resetsAt,
});
snapshot.providerId = "mistral";
snapshot.primary.descriptionIsDetail = true;
snapshot.selectedMetric.descriptionIsDetail = true;
snapshot.extraRateWindows = [
{
id: "mistral-monthly-plan",
title: "Monthly Plan",
window: {
...rateWindow(13, {
resetDescription: "34.07 EUR / 255.00 EUR · 220.93 EUR remaining",
resetsAt,
}),
descriptionIsDetail: true,
},
},
];

renderCard(snapshot);

const primaryDetail = await screen.findByText(
"19.17 EUR / 25.50 EUR · 6.33 EUR remaining",
);
const planDetail = screen.getByText("34.07 EUR / 255.00 EUR · 220.93 EUR remaining");
expect(primaryDetail).toHaveClass("menu-metric__detail");
expect(planDetail).toHaveClass("menu-metric__detail");
expect(screen.queryByText(/Resets .*EUR/)).not.toBeInTheDocument();
expect(document.querySelectorAll(".menu-metric__reset")).toHaveLength(hasReset ? 2 : 0);
},
);

it("renders informational metrics without quota percentages", async () => {
const snapshot = provider(null, 20);
snapshot.extraRateWindows = [
Expand Down
7 changes: 6 additions & 1 deletion apps/desktop-tauri/src/components/MenuCardDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { SimpleBarChart, StackedBarChart } from "./MiniBarChart";
import { InventoryItemRow } from "./InventoryRows";
import { QuotaWindowHistory } from "./QuotaWindowHistory";
import { getPaceBudget, type PaceBudget } from "../lib/paceBudget";
import { resetDescriptionFallback, windowDetailText } from "../lib/usageWindows";
import PaceDetailsChart from "./PaceDetailsChart";

/** Format a reserve description from raw pace data at render time. */
Expand Down Expand Up @@ -337,9 +338,10 @@ function MetricRow({
const barDisplayPct = showAsUsed ? barPct : Math.max(0, Math.min(100, remain));
const displayLabel = showAsUsed ? t("PanelUsedSuffix") : t("PanelLeftSuffix");
const level = levelOf(remain, snap.isExhausted);
const detailText = windowDetailText(snap);
const resetText = useFormattedResetTime(
snap.resetsAt,
isInformational ? null : snap.resetDescription,
isInformational ? null : resetDescriptionFallback(snap),
resetTimeRelative,
resetFormatMode ?? "reset",
);
Expand Down Expand Up @@ -380,6 +382,9 @@ function MetricRow({
<span className="menu-metric__reset">{resetText}</span>
)}
</div>
{!compactOverview && detailText && (
<div className="menu-metric__detail">{detailText}</div>
)}
{!compactOverview && !isInformational && snap.isExhausted && (
<div className="menu-metric__exhausted">{exhaustedLabel}</div>
)}
Expand Down
22 changes: 22 additions & 0 deletions apps/desktop-tauri/src/floatbar/FloatBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type RateWindowOptions = {
informational?: boolean;
resetsAt?: string | null;
resetDescription?: string | null;
descriptionIsDetail?: boolean;
};

function rateWindow(
Expand All @@ -61,6 +62,7 @@ function rateWindow(
resetDescription: opts.resetDescription ?? null,
isExhausted: opts.exhausted ?? false,
isInformational: opts.informational,
descriptionIsDetail: opts.descriptionIsDetail,
reservePercent: null,
reserveDescription: null,
};
Expand All @@ -76,6 +78,7 @@ function snapshot(
errorState?: ProviderUsageSnapshot["errorState"];
resetsAt?: string | null;
resetDescription?: string | null;
descriptionIsDetail?: boolean;
informational?: boolean;
secondary?: {
used: number;
Expand Down Expand Up @@ -325,6 +328,25 @@ describe("FloatBar", () => {
});
});

it("never shows a detail-backed description as reset text", async () => {
tauriMocks.getCachedProviders.mockResolvedValue([
snapshot("claude", "Claude", 13, {
resetDescription: "34.07 EUR / 255.00 EUR · 220.93 EUR remaining",
descriptionIsDetail: true,
}),
]);
tauriMocks.getSettingsSnapshot.mockResolvedValue(
settings({ floatBarShowResetInline: true }),
);

const { container } = renderFloatBar(bootstrap({ floatBarShowResetInline: true }));
await waitFor(() => {
const pill = container.querySelector(".floatbar__pill");
expect(pill?.getAttribute("title")).toBe("Claude: 13% used");
expect(container.querySelector(".floatbar__reset")).toBeNull();
});
});

it("keeps an informational primary window when no secondary window is available", async () => {
tauriMocks.getCachedProviders.mockResolvedValue([
snapshot("claude", "Claude", 10, { informational: true }),
Expand Down
3 changes: 2 additions & 1 deletion apps/desktop-tauri/src/floatbar/FloatBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import { ProviderIcon } from "../components/providers/ProviderIcon";
import { getProviderIcon } from "../components/providers/providerIcons";
import { describeProviderState } from "../lib/providerState";
import { resetDescriptionFallback } from "../lib/usageWindows";
import type {
BootstrapState,
ProviderLocalUsageSummary,
Expand Down Expand Up @@ -221,7 +222,7 @@ function ProviderPill({
const label = state.isProblem ? stateLabel : `${Math.round(displayPercent)}%`;
const resetText = useFormattedResetTime(
rateWindow.resetsAt,
rateWindow.resetDescription,
resetDescriptionFallback(rateWindow),
resetRelative,
);
const resetSuffix = resetText ? `\n${resetText}` : "";
Expand Down
35 changes: 34 additions & 1 deletion apps/desktop-tauri/src/lib/usageWindows.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { describe, expect, it } from "vitest";
import type { RateWindowSnapshot } from "../types/bridge";
import { selectSingleMetricUsageWindow } from "./usageWindows";
import {
resetDescriptionFallback,
selectSingleMetricUsageWindow,
windowDetailText,
} from "./usageWindows";

function rateWindow(
overrides: Partial<RateWindowSnapshot> = {},
Expand Down Expand Up @@ -46,3 +50,32 @@ describe("selectSingleMetricUsageWindow", () => {
expect(selectSingleMetricUsageWindow({ primary, secondary })).toBe(primary);
});
});

describe("detail-backed descriptions", () => {
const amounts = "34.07 EUR / 255.00 EUR · 220.93 EUR remaining";

it("keeps ordinary descriptions as the reset fallback and shows no detail line", () => {
const window = rateWindow({ resetDescription: "Resets in 2h" });

expect(resetDescriptionFallback(window)).toBe("Resets in 2h");
expect(windowDetailText(window)).toBeNull();
});

it("moves a detail-backed description out of the reset fallback", () => {
const window = rateWindow({ resetDescription: ` ${amounts} `, descriptionIsDetail: true });

expect(resetDescriptionFallback(window)).toBeNull();
expect(windowDetailText(window)).toBe(amounts);
});

it("ignores blank and informational detail-backed descriptions", () => {
expect(
windowDetailText(rateWindow({ resetDescription: " ", descriptionIsDetail: true })),
).toBeNull();
expect(
windowDetailText(
rateWindow({ resetDescription: amounts, descriptionIsDetail: true, isInformational: true }),
),
).toBeNull();
});
});
19 changes: 19 additions & 0 deletions apps/desktop-tauri/src/lib/usageWindows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,22 @@ export function selectSingleMetricUsageWindow(
? secondary
: primary;
}

/**
* Reset-wording fallback for a window. A detail-backed description (for
* example spend amounts) is never reset text, so it must not reach
* `normalizeResetDescription` through the reset formatter.
*/
export function resetDescriptionFallback(
window: Pick<RateWindowSnapshot, "resetDescription" | "descriptionIsDetail">,
): string | null {
return window.descriptionIsDetail ? null : window.resetDescription;
}

/** Secondary detail line for a detail-backed window, or null. */
export function windowDetailText(
window: Pick<RateWindowSnapshot, "resetDescription" | "descriptionIsDetail" | "isInformational">,
): string | null {
if (!window.descriptionIsDetail || window.isInformational) return null;
return window.resetDescription?.trim() || null;
}
Loading