diff --git a/src/tui.tsx b/src/tui.tsx index 5281f47..344c4f3 100644 --- a/src/tui.tsx +++ b/src/tui.tsx @@ -7,6 +7,12 @@ function percent(value: number) { return value < 0.1 && value > 0 ? "<0.1" : value.toFixed(value < 10 ? 1 : 0); } +function duration(seconds: number) { + if (seconds % 86_400 === 0) return `${seconds / 86_400}d`; + if (seconds % 3_600 === 0) return `${seconds / 3_600}h`; + return `${Math.round(seconds / 60)}m`; +} + function resetTiming(resetsAt: number, now: number) { if (!Number.isFinite(resetsAt) || resetsAt <= 0 || !Number.isFinite(now)) return "unavailable"; const remaining = resetsAt - now; @@ -31,7 +37,10 @@ export function formatQuotaWindow( window: QuotaWindow & { remaining: number; sessionConsumed: number }, now = Date.now(), ) { - return `${percent(window.remaining)}% · ~${percent(window.sessionConsumed)}pp · ${resetTiming(window.resetsAt, now)}`; + const label = window.label + ? `${window.label} ${duration(window.seconds)}` + : duration(window.seconds); + return `${label}: ${percent(window.remaining)}% · ~${percent(window.sessionConsumed)}pp · ${resetTiming(window.resetsAt, now)}`; } export async function loadQuotaStatus(load: () => Promise) { diff --git a/tests/quota.test.ts b/tests/quota.test.ts index 7a0a7ad..cd20e7d 100644 --- a/tests/quota.test.ts +++ b/tests/quota.test.ts @@ -429,7 +429,7 @@ test("sidebar text places reset timing beside latest remaining quota", () => { }, 100_000, ), - ).toBe("75% · ~1.5pp · 2d 5h"); + ).toBe("7d: 75% · ~1.5pp · 2d 5h"); }); test("sidebar reset timing uses compact future units and handles boundaries", () => { @@ -448,14 +448,14 @@ test("sidebar reset timing uses compact future units and handles boundaries", () now, ); - expect(format(2 * 3_600_000 + 15 * 60_000)).toBe("75% · ~1.5pp · 2h 15m"); - expect(format(3_600_000)).toBe("75% · ~1.5pp · 1h"); - expect(format(15 * 60_000)).toBe("75% · ~1.5pp · 15m"); - expect(format(60_000)).toBe("75% · ~1.5pp · 1m"); - expect(format(59_999)).toBe("75% · ~1.5pp · <1m"); + expect(format(2 * 3_600_000 + 15 * 60_000)).toBe("5h: 75% · ~1.5pp · 2h 15m"); + expect(format(3_600_000)).toBe("5h: 75% · ~1.5pp · 1h"); + expect(format(15 * 60_000)).toBe("5h: 75% · ~1.5pp · 15m"); + expect(format(60_000)).toBe("5h: 75% · ~1.5pp · 1m"); + expect(format(59_999)).toBe("5h: 75% · ~1.5pp · <1m"); }); -test("sidebar reset timing omits labels and distinguishes stale and unavailable timestamps", () => { +test("sidebar reset timing preserves labels and distinguishes stale and unavailable timestamps", () => { const window = { seconds: 18_000, id: "codex:primary", @@ -464,9 +464,11 @@ test("sidebar reset timing omits labels and distinguishes stale and unavailable remaining: 0.05, sessionConsumed: 1.5, }; - expect(formatQuotaWindow({ ...window, resetsAt: 999 }, 1_000)).toBe("<0.1% · ~1.5pp · due"); + expect(formatQuotaWindow({ ...window, resetsAt: 999 }, 1_000)).toBe( + "GPT-5.6 Luna 5h: <0.1% · ~1.5pp · due", + ); expect(formatQuotaWindow({ ...window, resetsAt: Number.NaN }, 1_000)).toBe( - "<0.1% · ~1.5pp · unavailable", + "GPT-5.6 Luna 5h: <0.1% · ~1.5pp · unavailable", ); }); diff --git a/tests/tui.test.ts b/tests/tui.test.ts index c3c788a..10552b8 100644 --- a/tests/tui.test.ts +++ b/tests/tui.test.ts @@ -84,7 +84,7 @@ test("built quota slot stays reactive across pending, empty, failure, update, an pending.resolve(status("plus", 75)); await setup.waitForFrame((frame) => frame.includes("ChatGPT quota (plus)")); - expect(setup.captureCharFrame()).toContain("75% · ~1.5pp · due"); + expect(setup.captureCharFrame()).toContain("5h: 75% · ~1.5pp · due"); responses.push(async () => null); notifyUpdated();