From 4f0a514ce65a098f0a19367e6fc5313677f31593 Mon Sep 17 00:00:00 2001
From: spoukyii <61633921+spoukyii@users.noreply.github.com>
Date: Tue, 3 Nov 2026 13:42:23 -0800
Subject: [PATCH 1/2] test(web): thread details usage tooltip labels a reset in
its final minute
Hovering a limit row in the thread details card shows when the limit resets.
These cases pin that label a minute out and thirty seconds out. The second
reads "Resets in 0m" on main.
---
.../chat/ThreadDetailsUsage.browser.test.tsx | 60 +++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100644 apps/web/src/components/chat/ThreadDetailsUsage.browser.test.tsx
diff --git a/apps/web/src/components/chat/ThreadDetailsUsage.browser.test.tsx b/apps/web/src/components/chat/ThreadDetailsUsage.browser.test.tsx
new file mode 100644
index 0000000000000..63ddfef8adad3
--- /dev/null
+++ b/apps/web/src/components/chat/ThreadDetailsUsage.browser.test.tsx
@@ -0,0 +1,60 @@
+import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts";
+import { expect, it, vi } from "vite-plus/test";
+
+import { act, fakeTimers, render } from "../../../test/render";
+import { ThreadDetailsUsage } from "./ThreadDetailsUsage";
+
+const NOW = "2026-11-03T12:00:00.000Z";
+const instanceId = ProviderInstanceId.make("codex");
+
+function codexResettingAt(resetsAt: string): ServerProvider {
+ return {
+ instanceId,
+ driver: ProviderDriverKind.make("codex"),
+ enabled: true,
+ installed: true,
+ version: "0.164.0",
+ status: "ready",
+ auth: { status: "authenticated" },
+ checkedAt: NOW,
+ models: [],
+ slashCommands: [],
+ skills: [],
+ usageLimits: {
+ checkedAt: NOW,
+ windows: [
+ {
+ id: "session",
+ kind: "session",
+ label: "5h",
+ usedPercent: 40,
+ resetsAt,
+ windowDurationMins: 300,
+ },
+ ],
+ },
+ };
+}
+
+it.each([
+ ["2026-11-03T12:05:00.000Z", "Resets in 5m · "],
+ ["2026-11-03T12:00:30.000Z", "Resets momentarily · "],
+])("labels a limit resetting at %s", async (resetsAt, label) => {
+ using _timers = fakeTimers();
+ vi.setSystemTime(new Date(NOW));
+ await using screen = await render(
+ ,
+ );
+
+ await act(async () => {
+ screen.getByLabelText(/^5h:/).element().focus();
+ await vi.advanceTimersByTimeAsync(1_000);
+ });
+ await vi.waitFor(() =>
+ expect(document.querySelector('[data-slot="tooltip-popup"]')?.textContent).toContain(label),
+ );
+});
From f8684884ef350bcbb689e7877f89fc7819843468 Mon Sep 17 00:00:00 2001
From: spoukyii <61633921+spoukyii@users.noreply.github.com>
Date: Tue, 3 Nov 2026 13:42:24 -0800
Subject: [PATCH 2/2] fix(web): thread details usage says a limit resets
momentarily in its final minute
The limit tooltip in the thread details card built its reset label from
formatDuration, which floors to whole minutes. In the last minute before a
reset the tooltip read "Resets in 0m". #17869 taught the shared
formatResetsIn to say "Resets momentarily" there, and every other usage
surface calls it, but this card was added later with its own copy of the rule.
The tooltip now calls formatResetsIn and capitalizes its "resets in" prefix to
start the line.
Bug: thread details usage tooltip reads "Resets in 0m" in the last minute before a limit resets
---
apps/web/src/components/chat/ThreadDetailsUsage.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/web/src/components/chat/ThreadDetailsUsage.tsx b/apps/web/src/components/chat/ThreadDetailsUsage.tsx
index 05211d6ace44e..7dba650e80525 100644
--- a/apps/web/src/components/chat/ThreadDetailsUsage.tsx
+++ b/apps/web/src/components/chat/ThreadDetailsUsage.tsx
@@ -6,7 +6,7 @@ import type {
} from "@t3tools/contracts";
import {
collectProviderUsageLimits,
- formatDuration,
+ formatResetsIn,
type LimitPace,
limitsNotice,
paceOf,
@@ -162,7 +162,7 @@ function LimitRow({
{window.resetsAt && resetsAt > now ? (
- Resets in {formatDuration(resetsAt - now)} ·{" "}
+ {formatResetsIn(window, now)?.replace("resets in", "Resets in")} ·{" "}
{formatUpcomingTimestamp(window.resetsAt, timestampFormat, now)}
) : null}