From 8f983851b40b111d29160c45b4e20ae9abf744f3 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Fri, 21 Aug 2026 16:22:26 +0200 Subject: [PATCH] feat(ui): Usage-Monitor zeigt Reset-Uhrzeit hinter der Restzeit --- src/ClaudeDo.Localization/locales/de.json | 2 +- src/ClaudeDo.Localization/locales/en.json | 2 +- .../Modals/UsageMonitorModalViewModel.cs | 4 ++- .../UsageMonitorModalViewModelTests.cs | 26 +++++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/ClaudeDo.Localization/locales/de.json b/src/ClaudeDo.Localization/locales/de.json index 2d03796d..8c54fc16 100644 --- a/src/ClaudeDo.Localization/locales/de.json +++ b/src/ClaudeDo.Localization/locales/de.json @@ -497,7 +497,7 @@ "staleGateHint": "Das Gate greift in diesem Zustand nicht.", "gateBlockedFormat": "Warteschlange pausiert — {0}", "throttleFormat": "Warteschlange gedrosselt: {0}/{1} Slots ({2})", - "resetIn": "Reset in {0}", + "resetIn": "Reset in {0} ({1})", "dragHint": "Marker ziehen oder Wert unten eintragen.", "legendSoft": "Soft · 2 Slots", "legendHard": "Hard · 1 Slot", diff --git a/src/ClaudeDo.Localization/locales/en.json b/src/ClaudeDo.Localization/locales/en.json index c92cf11b..e362dab0 100644 --- a/src/ClaudeDo.Localization/locales/en.json +++ b/src/ClaudeDo.Localization/locales/en.json @@ -497,7 +497,7 @@ "staleGateHint": "The gate does not apply while values are stale.", "gateBlockedFormat": "Queue paused — {0}", "throttleFormat": "Queue throttled: {0}/{1} slots ({2})", - "resetIn": "Reset in {0}", + "resetIn": "Reset in {0} ({1})", "dragHint": "Drag a marker, or type the value below.", "legendSoft": "Soft · 2 slots", "legendHard": "Hard · 1 slot", diff --git a/src/ClaudeDo.Ui/ViewModels/Modals/UsageMonitorModalViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Modals/UsageMonitorModalViewModel.cs index 339534a8..ea31529e 100644 --- a/src/ClaudeDo.Ui/ViewModels/Modals/UsageMonitorModalViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Modals/UsageMonitorModalViewModel.cs @@ -391,7 +391,9 @@ public sealed partial class UsageGaugeRowViewModel : ObservableObject public bool IsWarnSeverity => !string.Equals(Severity, "normal", StringComparison.OrdinalIgnoreCase); - public string ResetText => ResetsAt is { } r ? Loc.T("modals.usageMonitor.resetIn", FormatRemaining(r)) : ""; + public string ResetText => ResetsAt is { } r + ? Loc.T("modals.usageMonitor.resetIn", FormatRemaining(r), r.ToLocalTime().ToString("HH:mm")) + : ""; /// Live values from a fresh snapshot, without replacing the row instance mid-view. public void Update(string label, double percent, string severity, DateTimeOffset? resetsAt, diff --git a/tests/ClaudeDo.Ui.Tests/ViewModels/UsageMonitorModalViewModelTests.cs b/tests/ClaudeDo.Ui.Tests/ViewModels/UsageMonitorModalViewModelTests.cs index 699c075a..f0060d51 100644 --- a/tests/ClaudeDo.Ui.Tests/ViewModels/UsageMonitorModalViewModelTests.cs +++ b/tests/ClaudeDo.Ui.Tests/ViewModels/UsageMonitorModalViewModelTests.cs @@ -586,6 +586,32 @@ public class UsageMonitorModalViewModelTests Assert.Null(UsageThresholdDrag.Nearest(50, 65, 80, percent: 20, tolerancePercent: 5)); } + // ── Reset text ─────────────────────────────────────────────────────────── + + [Fact] + public async Task ResetText_WithResetsAt_IncludesLocalResetTime() + { + var resetsAt = DateTimeOffset.UtcNow.AddHours(2).AddMinutes(15); + var worker = new FakeWorker { Snapshot = Snapshot(new[] { Limit("session", resetsAt: resetsAt) }) }; + var vm = new UsageMonitorModalViewModel(worker); + await vm.LoadAsync(); + + var expectedClock = resetsAt.ToLocalTime().ToString("HH:mm"); + Assert.StartsWith("Reset in", vm.GaugeRows[0].ResetText); + Assert.EndsWith($"({expectedClock})", vm.GaugeRows[0].ResetText); + Assert.Matches(@"2 h 1[45] m", vm.GaugeRows[0].ResetText); + } + + [Fact] + public async Task ResetText_NoResetsAt_IsEmpty() + { + var worker = new FakeWorker { Snapshot = Snapshot(new[] { Limit("session", resetsAt: null) }) }; + var vm = new UsageMonitorModalViewModel(worker); + await vm.LoadAsync(); + + Assert.Equal("", vm.GaugeRows[0].ResetText); + } + // ── Stale / gate bands ─────────────────────────────────────────────────── [Fact]