tokmon 0.3.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +185 -128
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -46,41 +46,6 @@ import { createReadStream } from "fs";
|
|
|
46
46
|
import { createInterface } from "readline";
|
|
47
47
|
import { join as join2 } from "path";
|
|
48
48
|
import { homedir as homedir2 } from "os";
|
|
49
|
-
|
|
50
|
-
// src/format.ts
|
|
51
|
-
function currency(value) {
|
|
52
|
-
return `$${value.toFixed(2)}`;
|
|
53
|
-
}
|
|
54
|
-
function tokens(value) {
|
|
55
|
-
if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`;
|
|
56
|
-
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
57
|
-
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
|
|
58
|
-
return String(value);
|
|
59
|
-
}
|
|
60
|
-
function time(date) {
|
|
61
|
-
return date.toLocaleTimeString(void 0, {
|
|
62
|
-
hour: "2-digit",
|
|
63
|
-
minute: "2-digit",
|
|
64
|
-
second: "2-digit"
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
function minutes(mins) {
|
|
68
|
-
const h = Math.floor(mins / 60);
|
|
69
|
-
const m = Math.round(mins % 60);
|
|
70
|
-
return h > 0 ? `${h}h ${m}m` : `${m}m`;
|
|
71
|
-
}
|
|
72
|
-
function shortDate(iso) {
|
|
73
|
-
const [, m, d] = iso.split("-");
|
|
74
|
-
const months = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
75
|
-
return `${months[Number(m)]} ${Number(d).toString().padStart(2, " ")}`;
|
|
76
|
-
}
|
|
77
|
-
function col(s, w, align = "right") {
|
|
78
|
-
if (s.length > w) return s.slice(0, w - 1) + "~";
|
|
79
|
-
const spaces = " ".repeat(w - s.length);
|
|
80
|
-
return align === "right" ? spaces + s : s + spaces;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// src/data.ts
|
|
84
49
|
var PRICING = {
|
|
85
50
|
"claude-opus-4": { i: 5e-6, o: 25e-6, cc: 625e-8, cr: 5e-7 },
|
|
86
51
|
"claude-sonnet-4": { i: 3e-6, o: 15e-6, cc: 375e-8, cr: 3e-7 },
|
|
@@ -228,30 +193,16 @@ function monthLabel(ts) {
|
|
|
228
193
|
return new Date(ts).toISOString().slice(0, 7);
|
|
229
194
|
}
|
|
230
195
|
async function fetchDashboard() {
|
|
231
|
-
const now = Date.now();
|
|
232
196
|
const d = /* @__PURE__ */ new Date();
|
|
233
197
|
const monthStart = new Date(d.getFullYear(), d.getMonth(), 1).getTime();
|
|
234
198
|
const todayStart = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
235
199
|
const weekDay = d.getDay();
|
|
236
200
|
const weekStart = new Date(d.getFullYear(), d.getMonth(), d.getDate() - (weekDay === 0 ? 6 : weekDay - 1)).getTime();
|
|
237
201
|
const entries = await loadEntries(monthStart);
|
|
238
|
-
const fiveHoursAgo = now - 5 * 36e5;
|
|
239
|
-
const blockEntries = entries.filter((e) => e.ts >= fiveHoursAgo);
|
|
240
|
-
let block = null;
|
|
241
|
-
if (blockEntries.length > 0) {
|
|
242
|
-
const spent = blockEntries.reduce((s, e) => s + e.cost, 0);
|
|
243
|
-
const oldest = Math.min(...blockEntries.map((e) => e.ts));
|
|
244
|
-
const elapsedHrs = (now - oldest) / 36e5;
|
|
245
|
-
const burnRate = elapsedHrs > 0 ? spent / elapsedHrs : 0;
|
|
246
|
-
const remainMs = Math.max(0, oldest + 5 * 36e5 - now);
|
|
247
|
-
const percent = Math.min(100, (now - oldest) / (5 * 36e5) * 100);
|
|
248
|
-
block = { spent, projected: burnRate * 5, burnRate, percent, remaining: minutes(remainMs / 6e4) };
|
|
249
|
-
}
|
|
250
202
|
return {
|
|
251
203
|
today: sum(entries.filter((e) => e.ts >= todayStart)),
|
|
252
204
|
week: sum(entries.filter((e) => e.ts >= weekStart)),
|
|
253
|
-
month: sum(entries.filter((e) => e.ts >= monthStart))
|
|
254
|
-
block
|
|
205
|
+
month: sum(entries.filter((e) => e.ts >= monthStart))
|
|
255
206
|
};
|
|
256
207
|
}
|
|
257
208
|
async function fetchTable() {
|
|
@@ -265,12 +216,113 @@ async function fetchTable() {
|
|
|
265
216
|
};
|
|
266
217
|
}
|
|
267
218
|
|
|
219
|
+
// src/billing.ts
|
|
220
|
+
import { execFile as execFileCb } from "child_process";
|
|
221
|
+
import { promisify } from "util";
|
|
222
|
+
var execFile = promisify(execFileCb);
|
|
223
|
+
async function getAccessToken() {
|
|
224
|
+
if (process.platform === "darwin") {
|
|
225
|
+
try {
|
|
226
|
+
const { stdout } = await execFile("security", [
|
|
227
|
+
"find-generic-password",
|
|
228
|
+
"-s",
|
|
229
|
+
"Claude Code-credentials",
|
|
230
|
+
"-w"
|
|
231
|
+
], { timeout: 5e3 });
|
|
232
|
+
const creds = JSON.parse(stdout.trim());
|
|
233
|
+
return creds?.claudeAiOauth?.accessToken ?? null;
|
|
234
|
+
} catch {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
async function fetchBilling() {
|
|
241
|
+
const token = await getAccessToken();
|
|
242
|
+
if (!token) return null;
|
|
243
|
+
try {
|
|
244
|
+
const res = await fetch("https://api.anthropic.com/api/oauth/usage", {
|
|
245
|
+
headers: {
|
|
246
|
+
"Authorization": `Bearer ${token}`,
|
|
247
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
248
|
+
"User-Agent": "tokmon"
|
|
249
|
+
},
|
|
250
|
+
signal: AbortSignal.timeout(1e4)
|
|
251
|
+
});
|
|
252
|
+
if (!res.ok) return null;
|
|
253
|
+
const data = await res.json();
|
|
254
|
+
return {
|
|
255
|
+
session: data.five_hour ? {
|
|
256
|
+
utilization: data.five_hour.utilization,
|
|
257
|
+
resetsAt: formatReset(data.five_hour.resets_at)
|
|
258
|
+
} : null,
|
|
259
|
+
weekly: data.seven_day ? {
|
|
260
|
+
utilization: data.seven_day.utilization,
|
|
261
|
+
resetsAt: formatReset(data.seven_day.resets_at)
|
|
262
|
+
} : null,
|
|
263
|
+
sonnet: data.seven_day_sonnet ? {
|
|
264
|
+
utilization: data.seven_day_sonnet.utilization,
|
|
265
|
+
resetsAt: formatReset(data.seven_day_sonnet.resets_at)
|
|
266
|
+
} : null,
|
|
267
|
+
extraUsage: data.extra_usage?.is_enabled ? {
|
|
268
|
+
limit: data.extra_usage.monthly_limit / 100,
|
|
269
|
+
used: data.extra_usage.used_credits / 100
|
|
270
|
+
} : null
|
|
271
|
+
};
|
|
272
|
+
} catch {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function formatReset(iso) {
|
|
277
|
+
const d = new Date(iso);
|
|
278
|
+
const now = /* @__PURE__ */ new Date();
|
|
279
|
+
const diff = d.getTime() - now.getTime();
|
|
280
|
+
if (diff <= 0) return "now";
|
|
281
|
+
const mins = Math.round(diff / 6e4);
|
|
282
|
+
if (mins < 60) return `${mins}m`;
|
|
283
|
+
const hrs = Math.floor(mins / 60);
|
|
284
|
+
const m = mins % 60;
|
|
285
|
+
if (hrs < 24) return `${hrs}h ${m}m`;
|
|
286
|
+
const days = Math.floor(hrs / 24);
|
|
287
|
+
const h = hrs % 24;
|
|
288
|
+
return `${days}d ${h}h`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// src/format.ts
|
|
292
|
+
function currency(value) {
|
|
293
|
+
return `$${value.toFixed(2)}`;
|
|
294
|
+
}
|
|
295
|
+
function tokens(value) {
|
|
296
|
+
if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`;
|
|
297
|
+
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
298
|
+
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
|
|
299
|
+
return String(value);
|
|
300
|
+
}
|
|
301
|
+
function time(date) {
|
|
302
|
+
return date.toLocaleTimeString(void 0, {
|
|
303
|
+
hour: "2-digit",
|
|
304
|
+
minute: "2-digit",
|
|
305
|
+
second: "2-digit"
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
function shortDate(iso) {
|
|
309
|
+
const [, m, d] = iso.split("-");
|
|
310
|
+
const months = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
311
|
+
return `${months[Number(m)]} ${Number(d).toString().padStart(2, " ")}`;
|
|
312
|
+
}
|
|
313
|
+
function col(s, w, align = "right") {
|
|
314
|
+
if (s.length > w) return s.slice(0, w - 1) + "~";
|
|
315
|
+
const spaces = " ".repeat(w - s.length);
|
|
316
|
+
return align === "right" ? spaces + s : s + spaces;
|
|
317
|
+
}
|
|
318
|
+
|
|
268
319
|
// src/app.tsx
|
|
269
320
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
270
321
|
var TABS = ["Dashboard", "Table"];
|
|
271
322
|
var VIEWS = ["Daily", "Weekly", "Monthly"];
|
|
272
323
|
function App({ interval: cliInterval }) {
|
|
273
324
|
const [dashboard, setDashboard] = useState(null);
|
|
325
|
+
const [billing, setBilling] = useState(null);
|
|
274
326
|
const [table, setTable] = useState(null);
|
|
275
327
|
const [tableLoading, setTableLoading] = useState(false);
|
|
276
328
|
const [error, setError] = useState(null);
|
|
@@ -283,18 +335,17 @@ function App({ interval: cliInterval }) {
|
|
|
283
335
|
const [settingsCursor, setSettingsCursor] = useState(0);
|
|
284
336
|
const tableLoadedOnce = useRef(false);
|
|
285
337
|
const { stdout } = useStdout();
|
|
338
|
+
const { exit } = useApp();
|
|
286
339
|
const rows = stdout?.rows ?? 24;
|
|
287
340
|
const cols = stdout?.columns ?? 80;
|
|
288
341
|
const interval2 = cliInterval ?? (config2?.interval ?? 2) * 1e3;
|
|
342
|
+
const cfg = config2 ?? { interval: 2, clearScreen: true };
|
|
289
343
|
useEffect(() => {
|
|
290
344
|
loadConfig().then((c) => {
|
|
291
345
|
if (cliInterval) c = { ...c, interval: cliInterval / 1e3 };
|
|
292
346
|
setConfig(c);
|
|
293
347
|
});
|
|
294
348
|
}, []);
|
|
295
|
-
useEffect(() => {
|
|
296
|
-
if (stdout) stdout.write("\x1B[2J\x1B[H");
|
|
297
|
-
}, [tab, view, showSettings]);
|
|
298
349
|
useEffect(() => {
|
|
299
350
|
let active = true;
|
|
300
351
|
const load = async () => {
|
|
@@ -316,6 +367,19 @@ function App({ interval: cliInterval }) {
|
|
|
316
367
|
clearInterval(id);
|
|
317
368
|
};
|
|
318
369
|
}, [interval2]);
|
|
370
|
+
useEffect(() => {
|
|
371
|
+
let active = true;
|
|
372
|
+
const load = () => fetchBilling().then((b) => {
|
|
373
|
+
if (active && b) setBilling(b);
|
|
374
|
+
}).catch(() => {
|
|
375
|
+
});
|
|
376
|
+
load();
|
|
377
|
+
const id = setInterval(load, 12e4);
|
|
378
|
+
return () => {
|
|
379
|
+
active = false;
|
|
380
|
+
clearInterval(id);
|
|
381
|
+
};
|
|
382
|
+
}, []);
|
|
319
383
|
useEffect(() => {
|
|
320
384
|
if (tab !== 1) return;
|
|
321
385
|
if (tableLoadedOnce.current && table) return;
|
|
@@ -349,36 +413,29 @@ function App({ interval: cliInterval }) {
|
|
|
349
413
|
clearInterval(id);
|
|
350
414
|
};
|
|
351
415
|
}, [tab, interval2]);
|
|
352
|
-
const { exit } = useApp();
|
|
353
416
|
const isTTY = process.stdin.isTTY === true;
|
|
354
|
-
const settingsItems = 2;
|
|
355
|
-
const cfg = config2 ?? { interval: 2, clearScreen: true };
|
|
356
417
|
useInput((input, key) => {
|
|
357
418
|
if (showSettings) {
|
|
358
419
|
if (key.escape || input === "s") setShowSettings(false);
|
|
359
420
|
if (key.upArrow) setSettingsCursor((c) => Math.max(0, c - 1));
|
|
360
|
-
if (key.downArrow) setSettingsCursor((c) => Math.min(
|
|
421
|
+
if (key.downArrow) setSettingsCursor((c) => Math.min(1, c + 1));
|
|
361
422
|
if (settingsCursor === 0) {
|
|
362
|
-
if (key.leftArrow) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
saveConfig(next);
|
|
373
|
-
return next;
|
|
374
|
-
});
|
|
375
|
-
}
|
|
423
|
+
if (key.leftArrow) setConfig((c) => {
|
|
424
|
+
const n = { ...c, interval: Math.max(1, c.interval - 1) };
|
|
425
|
+
saveConfig(n);
|
|
426
|
+
return n;
|
|
427
|
+
});
|
|
428
|
+
if (key.rightArrow) setConfig((c) => {
|
|
429
|
+
const n = { ...c, interval: c.interval + 1 };
|
|
430
|
+
saveConfig(n);
|
|
431
|
+
return n;
|
|
432
|
+
});
|
|
376
433
|
}
|
|
377
434
|
if (settingsCursor === 1 && (key.leftArrow || key.rightArrow || key.return)) {
|
|
378
435
|
setConfig((c) => {
|
|
379
|
-
const
|
|
380
|
-
saveConfig(
|
|
381
|
-
return
|
|
436
|
+
const n = { ...c, clearScreen: !c.clearScreen };
|
|
437
|
+
saveConfig(n);
|
|
438
|
+
return n;
|
|
382
439
|
});
|
|
383
440
|
}
|
|
384
441
|
return;
|
|
@@ -447,7 +504,7 @@ function App({ interval: cliInterval }) {
|
|
|
447
504
|
if (error) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { color: "red", children: error }) });
|
|
448
505
|
if (!dashboard) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Loading..." }) });
|
|
449
506
|
const tableData = table ? [table.daily, table.weekly, table.monthly][view] : [];
|
|
450
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
507
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, minHeight: rows, children: [
|
|
451
508
|
/* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
|
|
452
509
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
453
510
|
/* @__PURE__ */ jsxs(Text, { bold: true, color: "greenBright", children: [
|
|
@@ -468,7 +525,7 @@ function App({ interval: cliInterval }) {
|
|
|
468
525
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " Tab/\u2190\u2192" })
|
|
469
526
|
] }),
|
|
470
527
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
471
|
-
tab === 0 && /* @__PURE__ */ jsx(DashboardView, { data: dashboard }),
|
|
528
|
+
tab === 0 && /* @__PURE__ */ jsx(DashboardView, { data: dashboard, billing }),
|
|
472
529
|
tab === 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
473
530
|
/* @__PURE__ */ jsx(ViewBar, { views: VIEWS, active: view }),
|
|
474
531
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
@@ -480,8 +537,7 @@ function App({ interval: cliInterval }) {
|
|
|
480
537
|
/* @__PURE__ */ jsx(Text, { children: "David Ilie" }),
|
|
481
538
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " (" }),
|
|
482
539
|
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "davidilie.com" }),
|
|
483
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ") \xB7 " })
|
|
484
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "s=settings q=quit" })
|
|
540
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ") \xB7 s=settings q=quit" })
|
|
485
541
|
] })
|
|
486
542
|
] });
|
|
487
543
|
}
|
|
@@ -542,7 +598,7 @@ function SettingsView({ config: config2, cursor }) {
|
|
|
542
598
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191\u2193 select \u2190\u2192 adjust s/Esc close" })
|
|
543
599
|
] });
|
|
544
600
|
}
|
|
545
|
-
function DashboardView({ data }) {
|
|
601
|
+
function DashboardView({ data, billing }) {
|
|
546
602
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
547
603
|
/* @__PURE__ */ jsxs(
|
|
548
604
|
Box,
|
|
@@ -563,9 +619,39 @@ function DashboardView({ data }) {
|
|
|
563
619
|
]
|
|
564
620
|
}
|
|
565
621
|
),
|
|
566
|
-
|
|
622
|
+
billing && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
567
623
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
568
|
-
/* @__PURE__ */
|
|
624
|
+
/* @__PURE__ */ jsxs(
|
|
625
|
+
Box,
|
|
626
|
+
{
|
|
627
|
+
flexDirection: "column",
|
|
628
|
+
paddingLeft: 1,
|
|
629
|
+
borderStyle: "bold",
|
|
630
|
+
borderColor: "yellow",
|
|
631
|
+
borderRight: false,
|
|
632
|
+
borderTop: false,
|
|
633
|
+
borderBottom: false,
|
|
634
|
+
children: [
|
|
635
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Rate Limits" }),
|
|
636
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
637
|
+
billing.session && /* @__PURE__ */ jsx(LimitBar, { label: "Session", pct: billing.session.utilization, resets: billing.session.resetsAt }),
|
|
638
|
+
billing.weekly && /* @__PURE__ */ jsx(LimitBar, { label: "Weekly", pct: billing.weekly.utilization, resets: billing.weekly.resetsAt }),
|
|
639
|
+
billing.sonnet && /* @__PURE__ */ jsx(LimitBar, { label: "Sonnet", pct: billing.sonnet.utilization, resets: billing.sonnet.resetsAt }),
|
|
640
|
+
billing.extraUsage && /* @__PURE__ */ jsxs(Box, { children: [
|
|
641
|
+
/* @__PURE__ */ jsx(Box, { width: 10, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Extra" }) }),
|
|
642
|
+
/* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
643
|
+
"$",
|
|
644
|
+
billing.extraUsage.used.toFixed(2)
|
|
645
|
+
] }),
|
|
646
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
647
|
+
" / $",
|
|
648
|
+
billing.extraUsage.limit.toFixed(2),
|
|
649
|
+
" limit"
|
|
650
|
+
] })
|
|
651
|
+
] })
|
|
652
|
+
]
|
|
653
|
+
}
|
|
654
|
+
)
|
|
569
655
|
] }),
|
|
570
656
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
571
657
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(50) }),
|
|
@@ -575,46 +661,24 @@ function DashboardView({ data }) {
|
|
|
575
661
|
] })
|
|
576
662
|
] });
|
|
577
663
|
}
|
|
578
|
-
function
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
] })
|
|
597
|
-
] }),
|
|
598
|
-
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
599
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
600
|
-
/* @__PURE__ */ jsx(ProgressBar, { percent: block.percent, width: 36 }),
|
|
601
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
602
|
-
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
603
|
-
Math.round(block.percent),
|
|
604
|
-
"%"
|
|
605
|
-
] })
|
|
606
|
-
] }),
|
|
607
|
-
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
608
|
-
/* @__PURE__ */ jsx(Text, { color: "yellow", children: currency(block.spent) }),
|
|
609
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " spent \xB7 ~" }),
|
|
610
|
-
/* @__PURE__ */ jsx(Text, { children: currency(block.projected) }),
|
|
611
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " proj \xB7 " }),
|
|
612
|
-
/* @__PURE__ */ jsx(Text, { color: "red", children: currency(block.burnRate) }),
|
|
613
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "/hr" })
|
|
614
|
-
] })
|
|
615
|
-
]
|
|
616
|
-
}
|
|
617
|
-
);
|
|
664
|
+
function LimitBar({ label, pct, resets }) {
|
|
665
|
+
const width = 30;
|
|
666
|
+
const filled = Math.round(pct / 100 * width);
|
|
667
|
+
const color = pct >= 80 ? "red" : pct >= 50 ? "yellow" : "green";
|
|
668
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
669
|
+
/* @__PURE__ */ jsx(Box, { width: 10, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: label }) }),
|
|
670
|
+
/* @__PURE__ */ jsx(Text, { color, children: "\u2501".repeat(filled) }),
|
|
671
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(width - filled) }),
|
|
672
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
673
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
674
|
+
Math.round(pct),
|
|
675
|
+
"%"
|
|
676
|
+
] }),
|
|
677
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
678
|
+
" resets ",
|
|
679
|
+
resets
|
|
680
|
+
] })
|
|
681
|
+
] });
|
|
618
682
|
}
|
|
619
683
|
function SummaryRow({ label, summary }) {
|
|
620
684
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
@@ -626,13 +690,6 @@ function SummaryRow({ label, summary }) {
|
|
|
626
690
|
] }) })
|
|
627
691
|
] });
|
|
628
692
|
}
|
|
629
|
-
function ProgressBar({ percent, width = 36 }) {
|
|
630
|
-
const filled = Math.round(percent / 100 * width);
|
|
631
|
-
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
632
|
-
/* @__PURE__ */ jsx(Text, { color: "greenBright", children: "\u2501".repeat(filled) }),
|
|
633
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(width - filled) })
|
|
634
|
-
] });
|
|
635
|
-
}
|
|
636
693
|
function TableView({ rows: allRows, scroll, maxRows, wide }) {
|
|
637
694
|
const W = wide ? { label: 10, models: 18, input: 8, output: 8, cc: 8, cr: 9, total: 9, cost: 10 } : { label: 8, models: 14, input: 7, output: 7, cc: 7, cr: 8, total: 0, cost: 9 };
|
|
638
695
|
const lineW = W.label + W.models + W.input + W.output + W.cc + W.cr + W.total + W.cost;
|