diff --git a/src/ClaudeDo.Ui/Services/UsageTimeFormat.cs b/src/ClaudeDo.Ui/Services/UsageTimeFormat.cs new file mode 100644 index 00000000..8432a843 --- /dev/null +++ b/src/ClaudeDo.Ui/Services/UsageTimeFormat.cs @@ -0,0 +1,19 @@ +using System; +using ClaudeDo.Ui.Localization; + +namespace ClaudeDo.Ui.Services; + +/// Shared "Xh Ym" formatting for usage-reset countdowns (pill tooltip + usage monitor gauges). +public static class UsageTimeFormat +{ + public static string FormatRemaining(DateTimeOffset resetsAt) + { + var remaining = resetsAt - DateTimeOffset.UtcNow; + if (remaining < TimeSpan.Zero) remaining = TimeSpan.Zero; + var hours = (int)remaining.TotalHours; + var minutes = remaining.Minutes; + return hours > 0 + ? Loc.T("usage.pill.durationHoursMinutes", hours, minutes) + : Loc.T("usage.pill.durationMinutes", minutes); + } +} diff --git a/src/ClaudeDo.Ui/ViewModels/Modals/UsageMonitorModalViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Modals/UsageMonitorModalViewModel.cs index ea31529e..4f3a8414 100644 --- a/src/ClaudeDo.Ui/ViewModels/Modals/UsageMonitorModalViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Modals/UsageMonitorModalViewModel.cs @@ -391,8 +391,10 @@ public sealed partial class UsageGaugeRowViewModel : ObservableObject public bool IsWarnSeverity => !string.Equals(Severity, "normal", StringComparison.OrdinalIgnoreCase); + // Includes the absolute HH:mm alongside the countdown, unlike the pill tooltip's shorter + // "resetIn" line (usage.pill.resetIn) — the modal has room for the detail, the tooltip doesn't. public string ResetText => ResetsAt is { } r - ? Loc.T("modals.usageMonitor.resetIn", FormatRemaining(r), r.ToLocalTime().ToString("HH:mm")) + ? Loc.T("modals.usageMonitor.resetIn", UsageTimeFormat.FormatRemaining(r), r.ToLocalTime().ToString("HH:mm")) : ""; /// Live values from a fresh snapshot, without replacing the row instance mid-view. @@ -437,17 +439,6 @@ public sealed partial class UsageGaugeRowViewModel : ObservableObject return Commit(); } - - private static string FormatRemaining(DateTimeOffset resetsAt) - { - var remaining = resetsAt - DateTimeOffset.UtcNow; - if (remaining < TimeSpan.Zero) remaining = TimeSpan.Zero; - var hours = (int)remaining.TotalHours; - var minutes = remaining.Minutes; - return hours > 0 - ? Loc.T("usage.pill.durationHoursMinutes", hours, minutes) - : Loc.T("usage.pill.durationMinutes", minutes); - } } /// diff --git a/src/ClaudeDo.Ui/ViewModels/UsagePillViewModel.cs b/src/ClaudeDo.Ui/ViewModels/UsagePillViewModel.cs index 787b06ae..9ab5d963 100644 --- a/src/ClaudeDo.Ui/ViewModels/UsagePillViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/UsagePillViewModel.cs @@ -96,9 +96,9 @@ public sealed partial class UsagePillViewModel : ViewModelBase var lines = new List(); if (s.FiveHourResetsAt is { } fiveReset) - lines.Add(Loc.T("usage.pill.resetIn", Loc.T("usage.pill.fiveHourLabel"), FormatRemaining(fiveReset))); + lines.Add(Loc.T("usage.pill.resetIn", Loc.T("usage.pill.fiveHourLabel"), UsageTimeFormat.FormatRemaining(fiveReset))); if (s.SevenDayResetsAt is { } sevenReset) - lines.Add(Loc.T("usage.pill.resetIn", Loc.T("usage.pill.sevenDayLabel"), FormatRemaining(sevenReset))); + lines.Add(Loc.T("usage.pill.resetIn", Loc.T("usage.pill.sevenDayLabel"), UsageTimeFormat.FormatRemaining(sevenReset))); if (s.IsGateBlocked && !string.IsNullOrEmpty(s.GateReason)) lines.Add(Loc.T("usage.pill.blockedReason", s.GateReason)); else if (s.EffectiveSlots < s.ConfiguredSlots) @@ -120,15 +120,4 @@ public sealed partial class UsagePillViewModel : ViewModelBase "seven_day" => Loc.T("usage.pill.sevenDayLabel"), _ => "", }; - - private static string FormatRemaining(DateTimeOffset resetsAt) - { - var remaining = resetsAt - DateTimeOffset.UtcNow; - if (remaining < TimeSpan.Zero) remaining = TimeSpan.Zero; - var hours = (int)remaining.TotalHours; - var minutes = remaining.Minutes; - return hours > 0 - ? Loc.T("usage.pill.durationHoursMinutes", hours, minutes) - : Loc.T("usage.pill.durationMinutes", minutes); - } }