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}