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
2 changes: 2 additions & 0 deletions apps/desktop-tauri/src-tauri/src/auto_resume_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ mod tests {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
}
}

Expand Down
69 changes: 69 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ pub struct RateWindowSnapshot {
/// `reset_description` is a detail line (for example spend amounts), not reset wording.
#[serde(default)]
pub description_is_detail: bool,
/// The reset is the next quota regeneration; the card says "Regenerates in".
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub reset_regenerates: bool,
/// The window feeds the tray and float bar only; the card draws no row for it.
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub hidden_on_card: bool,
#[serde(default)]
pub reserve_percent: Option<f64>,
#[serde(default)]
Expand Down Expand Up @@ -72,6 +78,8 @@ impl RateWindowSnapshot {
pace: None,
monthly_limit_block: None,
description_is_detail: rw.description_is_detail,
reset_regenerates: rw.reset_regenerates,
hidden_on_card: rw.hidden_on_card,
}
}

Expand Down Expand Up @@ -131,6 +139,9 @@ pub struct CostSnapshotBridge {
pub daily: Vec<CostDailyPointBridge>,
#[serde(default)]
pub always_visible: bool,
/// Provider-owned cost-block title as a locale key name; absent keeps the shared title.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub title_key: Option<String>,
}

fn default_currency() -> String {
Expand Down Expand Up @@ -656,6 +667,7 @@ impl ProviderUsageSnapshot {
})
.collect(),
always_visible: c.always_visible,
title_key: c.card_title_key.map(|key| key.name().to_string()),
}),
plan_name: usage.login_method.clone(),
account_email: usage.account_email.clone(),
Expand Down Expand Up @@ -708,6 +720,8 @@ impl ProviderUsageSnapshot {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
},
primary_label: Some(metadata.session_label.to_string()),
secondary: None,
Expand Down Expand Up @@ -1310,6 +1324,8 @@ mod tests {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
}
}

Expand Down Expand Up @@ -1450,4 +1466,57 @@ mod tests {
"{japanese}"
);
}

/// Provider-declared card wording (Synthetic's regen countdown, LithosAI's
/// card-hidden headline and cost titles) reaches the card.
#[test]
fn provider_card_wording_crosses_the_bridge() {
let regen =
RateWindowSnapshot::from_rate_window(&RateWindow::new(10.0).with_regenerating_reset());
let hidden = RateWindowSnapshot::from_rate_window(
&RateWindow::informational("Balance: $1.00").hidden_on_card(),
);
let regen_json = serde_json::to_value(&regen).unwrap();
let hidden_json = serde_json::to_value(&hidden).unwrap();
assert_eq!(regen_json["resetRegenerates"], true);
assert!(regen_json.get("hiddenOnCard").is_none());
assert_eq!(hidden_json["hiddenOnCard"], true);
assert!(hidden_json.get("resetRegenerates").is_none());

let metadata = codexbar::core::instantiate_provider(ProviderId::LithosAI)
.metadata()
.clone();
let cost = codexbar::core::CostSnapshot::new(0.0, "USD", "Prepaid credits")
.with_balance(4.0)
.with_card_title(codexbar::locale::LocaleKey::CreditsTitle);
let result = codexbar::core::ProviderFetchResult::new(
codexbar::core::UsageSnapshot::new(RateWindow::new(0.0)),
"web",
)
.with_cost(cost);
let snapshot = ProviderUsageSnapshot::from_fetch_result(
ProviderId::LithosAI,
&metadata,
&result,
None,
None,
);
let json = serde_json::to_value(&snapshot).unwrap();
assert_eq!(json["cost"]["titleKey"], "CreditsTitle");

let plain = codexbar::core::ProviderFetchResult::new(
codexbar::core::UsageSnapshot::new(RateWindow::new(0.0)),
"web",
)
.with_cost(codexbar::core::CostSnapshot::new(1.0, "USD", "This month"));
let json = serde_json::to_value(ProviderUsageSnapshot::from_fetch_result(
ProviderId::LithosAI,
&metadata,
&plain,
None,
None,
))
.unwrap();
assert!(json["cost"].get("titleKey").is_none());
}
}
2 changes: 2 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1691,6 +1691,8 @@ mod reset_backfill_tests {
reserve_eta_seconds: None,
pace: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
monthly_limit_block: None,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ mod tests {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
}
}

Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-tauri/src-tauri/src/powertoys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ mod tests {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
reserve_will_last_to_reset: false,
}
}
Expand Down
13 changes: 13 additions & 0 deletions apps/desktop-tauri/src-tauri/src/tray_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,8 @@ mod tests {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
},
primary_label: None,
secondary: secondary_percent.map(|pct| crate::commands::RateWindowSnapshot {
Expand All @@ -713,6 +715,8 @@ mod tests {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
}),
secondary_label: None,
secondary_label_key: None,
Expand All @@ -732,6 +736,8 @@ mod tests {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
}),
tertiary_label: None,
extra_rate_windows: Vec::new(),
Expand All @@ -753,6 +759,7 @@ mod tests {
account_id: None,
daily: Vec::new(),
always_visible: false,
title_key: None,
}),
plan_name: None,
account_email: None,
Expand Down Expand Up @@ -809,6 +816,8 @@ mod tests {
reserve_eta_seconds: None,
pace: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
monthly_limit_block: None,
},
}
Expand Down Expand Up @@ -1111,6 +1120,8 @@ mod tests {
reserve_eta_seconds: None,
pace: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
monthly_limit_block: None,
});
let (primary, _) = selected_tray_percents(&snapshot, &settings);
Expand Down Expand Up @@ -1152,6 +1163,8 @@ mod tests {
reserve_eta_seconds: None,
pace: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
monthly_limit_block: None,
});
snapshot.extra_rate_windows.push(fake_extra_window(90.0));
Expand Down
9 changes: 9 additions & 0 deletions apps/desktop-tauri/src-tauri/src/tray_presentation_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ fn fake_snapshot_with(
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
};

ProviderUsageSnapshot {
Expand Down Expand Up @@ -60,6 +62,7 @@ fn fake_snapshot_with(
account_id: None,
daily: Vec::new(),
always_visible: false,
title_key: None,
}),
plan_name: None,
account_email: None,
Expand Down Expand Up @@ -399,6 +402,8 @@ fn fake_extra_window(percent: f64) -> crate::commands::NamedRateWindowSnapshot {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
},
}
}
Expand Down Expand Up @@ -623,6 +628,8 @@ fn claude_automatic_prefers_weekly_when_model_exhausted() {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
});

let (primary, _) = selected_tray_percents(&snapshot, &settings);
Expand Down Expand Up @@ -669,6 +676,8 @@ fn automatic_picks_highest_among_model_and_extra_windows() {
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
});
snapshot.extra_rate_windows.push(fake_extra_window(90.0));

Expand Down
3 changes: 3 additions & 0 deletions apps/desktop-tauri/src-tauri/src/usage_metric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@ fn derived_window(used_percent: f64, resets_at: Option<String>) -> RateWindowSna
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reset_regenerates: false,
hidden_on_card: false,
}
}

Expand Down Expand Up @@ -869,6 +871,7 @@ mod tests {
formatted_balance: None,
daily: Vec::new(),
always_visible: false,
title_key: None,
});
let settings = metric_settings(ProviderId::Mistral, MetricPreference::MonthlyPlan);

Expand Down
118 changes: 118 additions & 0 deletions apps/desktop-tauri/src/components/MenuCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1369,6 +1369,124 @@ describe("MenuCard", () => {
expect(container.querySelector(".card-extra")).toBeNull();
});

describe("LithosAI shows its balance twice, as on the Mac", () => {
function lithos(balance: number | null, shown: string): ProviderUsageSnapshot {
const snapshot = provider(null, 0, { resetDescription: `Balance: ${shown}` });
snapshot.providerId = "lithosai";
snapshot.displayName = "LithosAI";
snapshot.primary = { ...snapshot.primary, isInformational: true, hiddenOnCard: true };
snapshot.cost =
balance === null
? null
: {
used: 0,
limit: null,
remaining: null,
currencyCode: "USD",
period: "Prepaid credits",
resetsAt: null,
formattedUsed: "$0.00",
formattedLimit: null,
balance,
formattedBalance: shown,
titleKey: "CreditsTitle",
};
snapshot.displayDetails = [
{ id: "balance", sectionTitle: "Billing", title: "Balance", value: shown, secondaryValue: null, progress: null },
{ id: "payment-card", sectionTitle: "Billing", title: "Payment card", value: "Added", secondaryValue: null, progress: null },
];
return snapshot;
}

beforeEach(() => {
tauriMocks.getLocaleStrings.mockResolvedValue(
buildBundle({ CreditsTitle: "Credits", ExtraUsage: "Extra usage", DetailCostBalance: "Balance" }),
);
});

// Upstream: the Billing "Balance" row (lithosai.ts:63), then the cost block
// (lithosai.ts:72-73, StatusItemController+Menu.swift:1307-1336,1377-1391),
// titled "Credits" and reading "Balance: $X" (MenuCardView+Costs.swift:511-518).
it("in the Billing row, then in the Credits block", async () => {
const { container } = renderCard(lithos(42.37, "$42.37"));

expect(await screen.findByText("Credits")).toHaveClass("card-extra__title");
const shown = screen.getAllByText(/42\.37/);
expect(shown).toHaveLength(2);
expect(shown[0]).toHaveTextContent(/^\$42\.37$/);
expect(shown[0]).toHaveClass("card-kv__value");
expect(shown[1]).toHaveTextContent(/^Balance: \$42\.37$/);
expect(shown[1]).toHaveClass("card-extra__inline-value");
expect(
shown[0].compareDocumentPosition(shown[1]) & Node.DOCUMENT_POSITION_FOLLOWING,
).toBeTruthy();
expect(screen.getByText("Payment card")).toBeInTheDocument();
// The headline balance stays off the card (tray and float bar only).
expect(container.querySelector(".menu-metric")).toBeNull();
expect(screen.queryByText("Extra usage")).not.toBeInTheDocument();
});

// Upstream compact Overview rows draw the provider details when a card has no
// metrics (MenuCardView+ModelHelpers.swift:378-380) and never the cost block.
it("once on the compact Overview, in the Billing rows", async () => {
const { container } = renderCard(lithos(42.37, "$42.37"), { compactOverview: true });

expect(await screen.findByText("Payment card")).toBeInTheDocument();
const shown = screen.getAllByText(/42\.37/);
expect(shown).toHaveLength(1);
expect(shown[0]).toHaveTextContent(/^\$42\.37$/);
expect(shown[0]).toHaveClass("card-kv__value");
expect(container.querySelector(".menu-metric")).toBeNull();
expect(container.querySelector(".card-extra")).toBeNull();
expect(container.querySelector(".menu-card--header-only")).toBeNull();
});

// Windows deviation: the Mac `.prepaidCredits` block ignores the summary style.
it("once, in the Billing row, when the cost summary is hidden", async () => {
const { container } = renderCard(lithos(42.37, "$42.37"), { costSummaryDisplayStyle: "hidden" });

expect(await screen.findByText("$42.37")).toBeInTheDocument();
expect(screen.getAllByText(/42\.37/)).toHaveLength(1);
expect(container.querySelector(".menu-metric")).toBeNull();
expect(container.querySelector(".card-extra")).toBeNull();
});

// Windows deviation: the shared cost block has no negative balance, so a debt has no block.
it("once, in the Billing row, for a debt", async () => {
const { container } = renderCard(lithos(null, "-$1.00"));

expect(await screen.findByText("-$1.00")).toBeInTheDocument();
expect(screen.getAllByText(/1\.00/)).toHaveLength(1);
expect(container.querySelector(".menu-metric")).toBeNull();
expect(container.querySelector(".card-extra")).toBeNull();
});
});

it("titles ClawRouter's capped cost block Monthly budget", async () => {
tauriMocks.getLocaleStrings.mockResolvedValue(
buildBundle({ CostTitleClawRouterMonthlyBudget: "Monthly budget", ExtraUsage: "Extra usage" }),
);
const snapshot = provider(null, 37);
snapshot.providerId = "clawrouter";
snapshot.displayName = "ClawRouter";
snapshot.cost = {
used: 18.42,
limit: 50,
remaining: 31.58,
currencyCode: "USD",
period: "This month",
resetsAt: null,
formattedUsed: "$18.42",
formattedLimit: "$50.00",
titleKey: "CostTitleClawRouterMonthlyBudget",
};

renderCard(snapshot);

expect(await screen.findByText("Monthly budget")).toHaveClass("card-extra__title");
expect(screen.queryByText("Extra usage")).not.toBeInTheDocument();
});

it("localizes the relative updated-at time in Japanese without duplicated prefix", async () => {
tauriMocks.getLocaleStrings.mockResolvedValue(
buildBundle({
Expand Down
Loading