From 7f627c431796f70c3e06c0cc0e5f66b97e30c672 Mon Sep 17 00:00:00 2001 From: Joseph Yaksich <294273268+gitcommit90@users.noreply.github.com> Date: Mon, 13 Jul 2026 22:33:33 +0000 Subject: [PATCH] Format billion-scale usage totals --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/question.yml | 2 +- README.md | 4 +-- package-lock.json | 4 +-- package.json | 2 +- scripts/capture-ui.js | 28 +++++++++++++++++--- src/renderer/app.js | 8 +----- src/renderer/index.html | 1 + src/renderer/number-format.js | 34 ++++++++++++++++++++++++ tests/number-format.test.js | 37 +++++++++++++++++++++++++++ 10 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 src/renderer/number-format.js create mode 100644 tests/number-format.test.js diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 206fb4f..99e8088 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -16,7 +16,7 @@ body: id: version attributes: label: ReRouted version - placeholder: "For example: 0.4.2" + placeholder: "Shown in Settings under Application" validations: required: true - type: input diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml index 4fdeb44..c1fbfa4 100644 --- a/.github/ISSUE_TEMPLATE/question.yml +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -14,7 +14,7 @@ body: id: version attributes: label: ReRouted version - placeholder: "For example: 0.4.2" + placeholder: "Shown in Settings under Application" validations: required: true - type: input diff --git a/README.md b/README.md index baa4bac..00214e8 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ ReRouted is an independent project and is not affiliated with or endorsed by any ### 1. Install -[Download ReRouted 0.4.2 for Apple Silicon](https://github.com/gitcommit90/rerouted/releases/download/v0.4.2/ReRouted-0.4.2-arm64.dmg), open the DMG, and drag ReRouted to Applications. +[Download ReRouted 0.4.3 for Apple Silicon](https://github.com/gitcommit90/rerouted/releases/download/v0.4.3/ReRouted-0.4.3-arm64.dmg), open the DMG, and drag ReRouted to Applications. ReRouted requires Apple Silicon and macOS 12 Monterey or newer. @@ -200,7 +200,7 @@ The implementation is intentionally small: Electron, Node's built-in HTTP server ## Current release -ReRouted `0.4.2` ships for Apple Silicon macOS with hardened OAuth callbacks and renderer lock boundaries, uncapped SQLite usage history, 60-second quota refreshes while the Quota page is open, masked account identities, and image inputs in chat completions. Public builds are Developer ID signed, notarized, stapled, and distributed through stable GitHub Releases with in-app updates. The public API is intentionally limited to health, model discovery, and chat completions; a published third-party client compatibility matrix is still forthcoming. +ReRouted `0.4.3` fixes compact usage metrics so billion and trillion totals display with `B` and `T` units instead of continuing to grow in millions. It includes the hardened OAuth callbacks and renderer lock boundaries, uncapped SQLite usage history, 60-second quota refreshes while the Quota page is open, masked account identities, and image inputs introduced in `0.4.2`. Public builds are Developer ID signed, notarized, stapled, and distributed through stable GitHub Releases with in-app updates. The public API is intentionally limited to health, model discovery, and chat completions; a published third-party client compatibility matrix is still forthcoming. ReRouted is an independent personal project. diff --git a/package-lock.json b/package-lock.json index 6b83c59..92dbc87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rerouted", - "version": "0.4.2", + "version": "0.4.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rerouted", - "version": "0.4.2", + "version": "0.4.3", "devDependencies": { "@electron/osx-sign": "1.3.3", "@electron/packager": "18.4.4", diff --git a/package.json b/package.json index 85bb5ac..b6d2a03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rerouted", - "version": "0.4.2", + "version": "0.4.3", "description": "A macOS menu-bar router for accounts, models, and automatic fallback.", "author": "gitcommit90", "homepage": "https://rerouted.dev", diff --git a/scripts/capture-ui.js b/scripts/capture-ui.js index 685caa1..2e364b3 100644 --- a/scripts/capture-ui.js +++ b/scripts/capture-ui.js @@ -63,10 +63,10 @@ function demoUsage(period = "24h") { requests: 148, ok: 143, errors: 5, - prompt_tokens: 184_200, - completion_tokens: 61_480, - cached_tokens: 92_600, - total_tokens: 338_280, + prompt_tokens: 1_486_000_000, + completion_tokens: 661_000_000, + cached_tokens: 483_648, + total_tokens: 2_147_483_648, byModel: [ { model: "coding", requests: 92, prompt_tokens: 121_000, completion_tokens: 42_300 }, { model: "chatgpt/gpt-5", requests: 38, prompt_tokens: 45_800, completion_tokens: 13_100 }, @@ -543,6 +543,26 @@ app.whenReady().then(async () => { settings: ".settings-group", }[p] || "#view > *"; await capture(`app-${p}.png`, selector); + if (p === "home") { + await win.webContents.executeJavaScript(` + (() => { + const value = document.querySelector("[data-home-tokens]")?.textContent.trim(); + if (value !== "2.1B") throw new Error("Unexpected billion token format: " + value); + return true; + })() + `); + } + if (p === "stats") { + await win.webContents.executeJavaScript(` + (() => { + const metrics = [...document.querySelectorAll(".metric")]; + const tokens = metrics.find((metric) => metric.querySelector(".metric-label")?.textContent.trim() === "Tokens"); + const value = tokens?.querySelector(".metric-value")?.textContent.trim(); + if (value !== "2.1B") throw new Error("Unexpected activity token format: " + value); + return true; + })() + `); + } if (p === "providers") { await win.webContents.executeJavaScript(` (() => { diff --git a/src/renderer/app.js b/src/renderer/app.js index b6c6f14..299cc22 100644 --- a/src/renderer/app.js +++ b/src/renderer/app.js @@ -3,6 +3,7 @@ const api = window.rerouted; const { accountDisplayName, accountIdentityLabel, maskAccountEmail } = window.ReroutedAccountIdentity; +const { compactNumber: fmtNum } = window.ReroutedNumberFormat; const { createLatestRequestGate, guardSensitiveRender } = window.ReroutedRendererLockState; const $ = (sel, el = document) => el.querySelector(sel); const view = $("#view"); @@ -800,13 +801,6 @@ function fmtTime(at) { } } -function fmtNum(n) { - const x = Number(n) || 0; - if (x >= 1_000_000) return (x / 1_000_000).toFixed(1).replace(/\.0$/, "") + "M"; - if (x >= 10_000) return (x / 1000).toFixed(1).replace(/\.0$/, "") + "k"; - return x.toLocaleString(); -} - function fmtRelativeTime(at) { if (!at) return "Never"; const seconds = Math.max(0, Math.round((Date.now() - Number(at)) / 1000)); diff --git a/src/renderer/index.html b/src/renderer/index.html index 337331c..20c9f08 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -59,6 +59,7 @@ + diff --git a/src/renderer/number-format.js b/src/renderer/number-format.js new file mode 100644 index 0000000..cdde7bc --- /dev/null +++ b/src/renderer/number-format.js @@ -0,0 +1,34 @@ +"use strict"; + +(function exposeNumberFormat(root) { + const UNITS = [ + { value: 1_000, suffix: "k" }, + { value: 1_000_000, suffix: "M" }, + { value: 1_000_000_000, suffix: "B" }, + { value: 1_000_000_000_000, suffix: "T" }, + ]; + + function compactNumber(value) { + const parsed = Number(value); + const number = Number.isFinite(parsed) ? parsed : 0; + const absolute = Math.abs(number); + if (absolute < 10_000) return number.toLocaleString("en-US"); + + let unitIndex = 0; + while (unitIndex < UNITS.length - 1 && absolute >= UNITS[unitIndex + 1].value) { + unitIndex += 1; + } + + let unit = UNITS[unitIndex]; + let rounded = Number((number / unit.value).toFixed(1)); + if (Math.abs(rounded) >= 1_000 && unitIndex < UNITS.length - 1) { + unit = UNITS[++unitIndex]; + rounded = Number((number / unit.value).toFixed(1)); + } + return `${rounded.toFixed(1).replace(/\.0$/, "")}${unit.suffix}`; + } + + const api = { compactNumber }; + if (typeof module !== "undefined" && module.exports) module.exports = api; + if (root) root.ReroutedNumberFormat = api; +})(typeof window !== "undefined" ? window : null); diff --git a/tests/number-format.test.js b/tests/number-format.test.js new file mode 100644 index 0000000..b5935cb --- /dev/null +++ b/tests/number-format.test.js @@ -0,0 +1,37 @@ +"use strict"; + +const assert = require("node:assert/strict"); +const { describe, it } = require("node:test"); +const { compactNumber } = require("../src/renderer/number-format"); + +describe("compact usage number formatting", () => { + it("uses the expected suffix at each magnitude", () => { + assert.equal(compactNumber(0), "0"); + assert.equal(compactNumber(9_999), "9,999"); + assert.equal(compactNumber(10_000), "10k"); + assert.equal(compactNumber(12_345), "12.3k"); + assert.equal(compactNumber(1_000_000), "1M"); + assert.equal(compactNumber(12_345_678), "12.3M"); + assert.equal(compactNumber(1_000_000_000), "1B"); + assert.equal(compactNumber(12_345_678_901), "12.3B"); + assert.equal(compactNumber(1_000_000_000_000), "1T"); + assert.equal(compactNumber(12_345_678_901_234), "12.3T"); + }); + + it("promotes values that round into the next unit", () => { + assert.equal(compactNumber(999_949), "999.9k"); + assert.equal(compactNumber(999_950), "1M"); + assert.equal(compactNumber(999_949_999), "999.9M"); + assert.equal(compactNumber(999_950_000), "1B"); + assert.equal(compactNumber(999_949_999_999), "999.9B"); + assert.equal(compactNumber(999_950_000_000), "1T"); + }); + + it("handles exact, negative, and invalid values", () => { + assert.equal(compactNumber(2_000_000_000), "2B"); + assert.equal(compactNumber(-2_500_000_000), "-2.5B"); + assert.equal(compactNumber("1234567890"), "1.2B"); + assert.equal(compactNumber(Number.NaN), "0"); + assert.equal(compactNumber(Number.POSITIVE_INFINITY), "0"); + }); +});