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);
- }
}