From 15650d9bb79959c8e36e04bd7036c688d85f79fd Mon Sep 17 00:00:00 2001 From: StanPonomarev Date: Mon, 14 Sep 2026 21:34:02 +0200 Subject: [PATCH] fix: restore session usage --- src/tui.tsx | 29 +++++++++++------------------ tests/quota.test.ts | 23 ++++++++++++----------- tests/tui.test.ts | 2 +- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/tui.tsx b/src/tui.tsx index 32357e9..5281f47 100644 --- a/src/tui.tsx +++ b/src/tui.tsx @@ -3,42 +3,35 @@ import { createResource, createSignal, For, Show } from "solid-js"; import type { QuotaStatus, QuotaWindow } from "./quota.js"; import { QuotaRpc } from "./quota-rpc.js"; -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 percent(value: number) { return value < 0.1 && value > 0 ? "<0.1" : value.toFixed(value < 10 ? 1 : 0); } function resetTiming(resetsAt: number, now: number) { - if (!Number.isFinite(resetsAt) || resetsAt <= 0 || !Number.isFinite(now)) - return "reset unavailable"; + if (!Number.isFinite(resetsAt) || resetsAt <= 0 || !Number.isFinite(now)) return "unavailable"; const remaining = resetsAt - now; - if (remaining <= 0) return "reset due"; - if (remaining < 60_000) return "resets in <1m"; + if (remaining <= 0) return "due"; + if (remaining < 60_000) return "<1m"; const minutes = Math.floor(remaining / 60_000); const days = Math.floor(minutes / 1_440); if (days > 0) { const hours = Math.floor((minutes % 1_440) / 60); - return `resets in ${days}d${hours > 0 ? ` ${hours}h` : ""}`; + return `${days}d${hours > 0 ? ` ${hours}h` : ""}`; } const hours = Math.floor(minutes / 60); if (hours > 0) { const rest = minutes % 60; - return `resets in ${hours}h${rest > 0 ? ` ${rest}m` : ""}`; + return `${hours}h${rest > 0 ? ` ${rest}m` : ""}`; } - return `resets in ${minutes}m`; + return `${minutes}m`; } -export function formatQuotaWindow(window: QuotaWindow & { remaining: number }, now = Date.now()) { - const label = window.label - ? `${window.label} ${duration(window.seconds)}` - : duration(window.seconds); - return `${label}: ${percent(window.remaining)}% left · ${resetTiming(window.resetsAt, now)}`; +export function formatQuotaWindow( + window: QuotaWindow & { remaining: number; sessionConsumed: number }, + now = Date.now(), +) { + return `${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 6166970..7a0a7ad 100644 --- a/tests/quota.test.ts +++ b/tests/quota.test.ts @@ -424,11 +424,12 @@ test("sidebar text places reset timing beside latest remaining quota", () => { label: null, used: 25, remaining: 75, + sessionConsumed: 1.5, resetsAt: 100_000 + 2 * 86_400_000 + 5 * 3_600_000, }, 100_000, ), - ).toBe("7d: 75% left · resets in 2d 5h"); + ).toBe("75% · ~1.5pp · 2d 5h"); }); test("sidebar reset timing uses compact future units and handles boundaries", () => { @@ -441,31 +442,31 @@ test("sidebar reset timing uses compact future units and handles boundaries", () label: null, used: 25, remaining: 75, + sessionConsumed: 1.5, resetsAt: now + resetIn, }, now, ); - expect(format(2 * 3_600_000 + 15 * 60_000)).toBe("5h: 75% left · resets in 2h 15m"); - expect(format(3_600_000)).toBe("5h: 75% left · resets in 1h"); - expect(format(15 * 60_000)).toBe("5h: 75% left · resets in 15m"); - expect(format(60_000)).toBe("5h: 75% left · resets in 1m"); - expect(format(59_999)).toBe("5h: 75% left · resets in <1m"); + 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"); }); -test("sidebar reset timing distinguishes stale and unavailable timestamps", () => { +test("sidebar reset timing omits labels and distinguishes stale and unavailable timestamps", () => { const window = { seconds: 18_000, id: "codex:primary", label: "GPT-5.6 Luna", used: 99.95, remaining: 0.05, + sessionConsumed: 1.5, }; - expect(formatQuotaWindow({ ...window, resetsAt: 999 }, 1_000)).toBe( - "GPT-5.6 Luna 5h: <0.1% left · reset due", - ); + expect(formatQuotaWindow({ ...window, resetsAt: 999 }, 1_000)).toBe("<0.1% · ~1.5pp · due"); expect(formatQuotaWindow({ ...window, resetsAt: Number.NaN }, 1_000)).toBe( - "GPT-5.6 Luna 5h: <0.1% left · reset unavailable", + "<0.1% · ~1.5pp · unavailable", ); }); diff --git a/tests/tui.test.ts b/tests/tui.test.ts index 54ee7b6..c3c788a 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("5h: 75% left · reset due"); + expect(setup.captureCharFrame()).toContain("75% · ~1.5pp · due"); responses.push(async () => null); notifyUpdated(); -- 2.54.0