tokmon 0.11.3 → 0.12.1
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 +801 -96
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,15 @@ import { MouseProvider } from "@zenobius/ink-mouse";
|
|
|
8
8
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
9
9
|
import { join } from "path";
|
|
10
10
|
import { homedir } from "os";
|
|
11
|
-
var DEFAULTS = {
|
|
11
|
+
var DEFAULTS = {
|
|
12
|
+
interval: 2,
|
|
13
|
+
billingInterval: 5,
|
|
14
|
+
clearScreen: true,
|
|
15
|
+
timezone: null,
|
|
16
|
+
accounts: [],
|
|
17
|
+
activeAccountId: null
|
|
18
|
+
};
|
|
19
|
+
var ACCENT_COLORS = ["cyan", "magenta", "green", "yellow", "blue", "red"];
|
|
12
20
|
function configDir() {
|
|
13
21
|
if (process.platform === "win32") {
|
|
14
22
|
return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "tokmon");
|
|
@@ -23,7 +31,12 @@ async function loadConfig() {
|
|
|
23
31
|
try {
|
|
24
32
|
const raw = await readFile(configLocation(), "utf-8");
|
|
25
33
|
const parsed = JSON.parse(raw);
|
|
26
|
-
return {
|
|
34
|
+
return {
|
|
35
|
+
...DEFAULTS,
|
|
36
|
+
...parsed,
|
|
37
|
+
accounts: Array.isArray(parsed.accounts) ? parsed.accounts : [],
|
|
38
|
+
activeAccountId: typeof parsed.activeAccountId === "string" ? parsed.activeAccountId : null
|
|
39
|
+
};
|
|
27
40
|
} catch {
|
|
28
41
|
return { ...DEFAULTS };
|
|
29
42
|
}
|
|
@@ -33,6 +46,32 @@ async function saveConfig(config2) {
|
|
|
33
46
|
await mkdir(dir, { recursive: true });
|
|
34
47
|
await writeFile(configLocation(), JSON.stringify(config2, null, 2) + "\n");
|
|
35
48
|
}
|
|
49
|
+
function slugify(value) {
|
|
50
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 48);
|
|
51
|
+
}
|
|
52
|
+
function generateAccountId(name, existing) {
|
|
53
|
+
const base = slugify(name) || "account";
|
|
54
|
+
const taken = new Set(existing.map((a) => a.id));
|
|
55
|
+
if (!taken.has(base)) return base;
|
|
56
|
+
for (let i = 2; i < 1e3; i++) {
|
|
57
|
+
const candidate = `${base}_${i}`;
|
|
58
|
+
if (!taken.has(candidate)) return candidate;
|
|
59
|
+
}
|
|
60
|
+
return `${base}_${Date.now()}`;
|
|
61
|
+
}
|
|
62
|
+
function pickAccentColor(existing) {
|
|
63
|
+
const used = new Set(existing.map((a) => a.color).filter(Boolean));
|
|
64
|
+
for (const c of ACCENT_COLORS) {
|
|
65
|
+
if (!used.has(c)) return c;
|
|
66
|
+
}
|
|
67
|
+
return ACCENT_COLORS[existing.length % ACCENT_COLORS.length];
|
|
68
|
+
}
|
|
69
|
+
function expandHome(p) {
|
|
70
|
+
if (!p) return homedir();
|
|
71
|
+
if (p === "~" || p === "~/") return homedir();
|
|
72
|
+
if (p.startsWith("~/")) return join(homedir(), p.slice(2));
|
|
73
|
+
return p;
|
|
74
|
+
}
|
|
36
75
|
|
|
37
76
|
// src/app.tsx
|
|
38
77
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
@@ -163,7 +202,13 @@ var PRICING = {
|
|
|
163
202
|
};
|
|
164
203
|
var FALLBACK = PRICING["claude-opus-4"];
|
|
165
204
|
var fileCache = /* @__PURE__ */ new Map();
|
|
166
|
-
function getClaudeDirs() {
|
|
205
|
+
function getClaudeDirs(homeDir) {
|
|
206
|
+
if (homeDir) {
|
|
207
|
+
return [
|
|
208
|
+
join2(homeDir, ".claude", "projects"),
|
|
209
|
+
join2(homeDir, ".config", "claude", "projects")
|
|
210
|
+
];
|
|
211
|
+
}
|
|
167
212
|
const home = homedir2();
|
|
168
213
|
const dirs = [join2(home, ".claude", "projects")];
|
|
169
214
|
if (process.env.XDG_CONFIG_HOME) {
|
|
@@ -220,10 +265,10 @@ async function parseFile(path, since) {
|
|
|
220
265
|
}
|
|
221
266
|
return entries;
|
|
222
267
|
}
|
|
223
|
-
async function loadEntries(since) {
|
|
268
|
+
async function loadEntries(since, homeDir) {
|
|
224
269
|
const chunks = [];
|
|
225
270
|
const seen = /* @__PURE__ */ new Set();
|
|
226
|
-
for (const dir of getClaudeDirs()) {
|
|
271
|
+
for (const dir of getClaudeDirs(homeDir)) {
|
|
227
272
|
let listing;
|
|
228
273
|
try {
|
|
229
274
|
listing = await readdir(dir, { recursive: true });
|
|
@@ -318,12 +363,12 @@ function groupBy(entries, keyFn) {
|
|
|
318
363
|
}
|
|
319
364
|
return rows.sort((a, b) => a.label.localeCompare(b.label));
|
|
320
365
|
}
|
|
321
|
-
async function fetchDashboard(tz) {
|
|
366
|
+
async function fetchDashboard(tz, homeDir) {
|
|
322
367
|
const now = Date.now();
|
|
323
368
|
const monthStart = startOfMonth(now, tz);
|
|
324
369
|
const todayStart = startOfDay(now, tz);
|
|
325
370
|
const weekStart = startOfWeek(now, tz);
|
|
326
|
-
const entries = await loadEntries(monthStart);
|
|
371
|
+
const entries = await loadEntries(monthStart, homeDir);
|
|
327
372
|
const todayEntries = entries.filter((e) => e.ts >= todayStart);
|
|
328
373
|
let burnRate = 0;
|
|
329
374
|
if (todayEntries.length > 0) {
|
|
@@ -343,9 +388,9 @@ async function fetchDashboard(tz) {
|
|
|
343
388
|
burnRate
|
|
344
389
|
};
|
|
345
390
|
}
|
|
346
|
-
async function fetchTable(tz) {
|
|
391
|
+
async function fetchTable(tz, homeDir) {
|
|
347
392
|
const lookback = monthsAgoStart(Date.now(), 6, tz);
|
|
348
|
-
const entries = await loadEntries(lookback);
|
|
393
|
+
const entries = await loadEntries(lookback, homeDir);
|
|
349
394
|
return {
|
|
350
395
|
daily: groupBy(entries, (e) => dayKey(e.ts, tz)),
|
|
351
396
|
weekly: groupBy(entries, (e) => weekKey(e.ts, tz)),
|
|
@@ -360,13 +405,14 @@ import { join as join3 } from "path";
|
|
|
360
405
|
import { homedir as homedir3 } from "os";
|
|
361
406
|
import { promisify } from "util";
|
|
362
407
|
var execFile = promisify(execFileCb);
|
|
363
|
-
function credentialsFilePath() {
|
|
408
|
+
function credentialsFilePath(homeDir) {
|
|
409
|
+
if (homeDir) return join3(homeDir, ".claude", ".credentials.json");
|
|
364
410
|
const base = process.env.CLAUDE_CONFIG_DIR ?? join3(homedir3(), ".claude");
|
|
365
411
|
return join3(base, ".credentials.json");
|
|
366
412
|
}
|
|
367
|
-
async function readCredentialsFile() {
|
|
413
|
+
async function readCredentialsFile(homeDir) {
|
|
368
414
|
try {
|
|
369
|
-
const raw = await readFile2(credentialsFilePath(), "utf-8");
|
|
415
|
+
const raw = await readFile2(credentialsFilePath(homeDir), "utf-8");
|
|
370
416
|
const creds = JSON.parse(raw);
|
|
371
417
|
return creds?.claudeAiOauth?.accessToken ?? creds?.accessToken ?? null;
|
|
372
418
|
} catch {
|
|
@@ -387,7 +433,12 @@ async function readMacKeychain() {
|
|
|
387
433
|
return null;
|
|
388
434
|
}
|
|
389
435
|
}
|
|
390
|
-
async function getAccessToken() {
|
|
436
|
+
async function getAccessToken(homeDir) {
|
|
437
|
+
if (homeDir) {
|
|
438
|
+
const fromFile = await readCredentialsFile(homeDir);
|
|
439
|
+
if (fromFile) return fromFile;
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
391
442
|
if (process.platform === "darwin") {
|
|
392
443
|
const token = await readMacKeychain();
|
|
393
444
|
if (token) return token;
|
|
@@ -395,8 +446,8 @@ async function getAccessToken() {
|
|
|
395
446
|
return readCredentialsFile();
|
|
396
447
|
}
|
|
397
448
|
var EMPTY = { session: null, weekly: null, sonnet: null, extraUsage: null, peak: null, error: null };
|
|
398
|
-
async function fetchBilling() {
|
|
399
|
-
const token = await getAccessToken();
|
|
449
|
+
async function fetchBilling(homeDir) {
|
|
450
|
+
const token = await getAccessToken(homeDir);
|
|
400
451
|
if (!token) return { ...EMPTY, error: "No OAuth token \u2014 run claude and log in" };
|
|
401
452
|
const [usageRes, peak] = await Promise.all([
|
|
402
453
|
fetchUsage(token),
|
|
@@ -519,12 +570,36 @@ var VIEWS = ["Daily", "Weekly", "Monthly"];
|
|
|
519
570
|
var SORTS = ["date \u2191", "date \u2193", "cost \u2191", "cost \u2193"];
|
|
520
571
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
521
572
|
var MONTHS = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
522
|
-
var DEFAULT_CONFIG = {
|
|
523
|
-
|
|
573
|
+
var DEFAULT_CONFIG = {
|
|
574
|
+
interval: 2,
|
|
575
|
+
billingInterval: 5,
|
|
576
|
+
clearScreen: true,
|
|
577
|
+
timezone: null,
|
|
578
|
+
accounts: [],
|
|
579
|
+
activeAccountId: null
|
|
580
|
+
};
|
|
581
|
+
var GENERAL_ROWS = 4;
|
|
524
582
|
var IS_TTY = process.stdin.isTTY === true;
|
|
583
|
+
function buildSlots(config2) {
|
|
584
|
+
const slots = [];
|
|
585
|
+
if (config2.accounts.length === 0) {
|
|
586
|
+
slots.push({ id: null, name: "Default", homeDir: void 0, color: "green" });
|
|
587
|
+
return slots;
|
|
588
|
+
}
|
|
589
|
+
slots.push({ id: null, name: "All", homeDir: void 0, color: "whiteBright" });
|
|
590
|
+
for (const a of config2.accounts) {
|
|
591
|
+
slots.push({
|
|
592
|
+
id: a.id,
|
|
593
|
+
name: a.name,
|
|
594
|
+
homeDir: expandHome(a.homeDir),
|
|
595
|
+
color: a.color || "cyan"
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
return slots;
|
|
599
|
+
}
|
|
525
600
|
function App({ interval: cliInterval }) {
|
|
526
|
-
const [
|
|
527
|
-
const [
|
|
601
|
+
const [config2, setConfig] = useState(null);
|
|
602
|
+
const [stats, setStats] = useState(/* @__PURE__ */ new Map());
|
|
528
603
|
const [table, setTable] = useState(null);
|
|
529
604
|
const [tableLoading, setTableLoading] = useState(false);
|
|
530
605
|
const [error, setError] = useState(null);
|
|
@@ -535,18 +610,28 @@ function App({ interval: cliInterval }) {
|
|
|
535
610
|
const [expanded, setExpanded] = useState(-1);
|
|
536
611
|
const [sort, setSort] = useState(1);
|
|
537
612
|
const [showSettings, setShowSettings] = useState(false);
|
|
538
|
-
const [config2, setConfig] = useState(null);
|
|
539
613
|
const [settingsCursor, setSettingsCursor] = useState(0);
|
|
540
614
|
const [tzEdit, setTzEdit] = useState(null);
|
|
541
615
|
const [tzError, setTzError] = useState(null);
|
|
616
|
+
const [accountForm, setAccountForm] = useState(null);
|
|
542
617
|
const tableLoadedOnce = useRef(false);
|
|
543
618
|
const { stdout } = useStdout();
|
|
544
619
|
const { exit } = useApp();
|
|
545
620
|
const rows = stdout?.rows ?? 24;
|
|
546
621
|
const cols = stdout?.columns ?? 80;
|
|
547
|
-
const interval2 = cliInterval ?? (config2?.interval ?? 2) * 1e3;
|
|
548
622
|
const cfg = config2 ?? DEFAULT_CONFIG;
|
|
623
|
+
const interval2 = cliInterval ?? cfg.interval * 1e3;
|
|
549
624
|
const tz = resolveTimezone(cfg.timezone);
|
|
625
|
+
const slots = buildSlots(cfg);
|
|
626
|
+
const activeSlotIdx = (() => {
|
|
627
|
+
if (cfg.accounts.length === 0) return 0;
|
|
628
|
+
if (cfg.activeAccountId === null) return 0;
|
|
629
|
+
const i = slots.findIndex((s) => s.id === cfg.activeAccountId);
|
|
630
|
+
return i < 0 ? 0 : i;
|
|
631
|
+
})();
|
|
632
|
+
const activeSlot = slots[activeSlotIdx];
|
|
633
|
+
const slotKey = (s) => s.id ?? "__default__";
|
|
634
|
+
const visibleSlots = activeSlot.id === null && cfg.accounts.length > 0 ? slots.slice(1) : [activeSlot];
|
|
550
635
|
useEffect(() => {
|
|
551
636
|
loadConfig().then((c) => {
|
|
552
637
|
if (cliInterval) c = { ...c, interval: cliInterval / 1e3 };
|
|
@@ -554,12 +639,21 @@ function App({ interval: cliInterval }) {
|
|
|
554
639
|
});
|
|
555
640
|
}, []);
|
|
556
641
|
useEffect(() => {
|
|
642
|
+
if (!config2) return;
|
|
557
643
|
let active = true;
|
|
644
|
+
const slotsToLoad = activeSlot.id === null && cfg.accounts.length > 0 ? slots.slice(1) : [activeSlot];
|
|
558
645
|
const load = async () => {
|
|
559
646
|
try {
|
|
560
|
-
const
|
|
647
|
+
const next = /* @__PURE__ */ new Map();
|
|
648
|
+
await Promise.all(slotsToLoad.map(async (slot) => {
|
|
649
|
+
const [dashboard, billing] = await Promise.all([
|
|
650
|
+
fetchDashboard(tz, slot.homeDir),
|
|
651
|
+
fetchBilling(slot.homeDir)
|
|
652
|
+
]);
|
|
653
|
+
next.set(slotKey(slot), { slot, dashboard, billing });
|
|
654
|
+
}));
|
|
561
655
|
if (active) {
|
|
562
|
-
|
|
656
|
+
setStats(next);
|
|
563
657
|
setError(null);
|
|
564
658
|
setUpdated(/* @__PURE__ */ new Date());
|
|
565
659
|
}
|
|
@@ -573,31 +667,17 @@ function App({ interval: cliInterval }) {
|
|
|
573
667
|
active = false;
|
|
574
668
|
clearInterval(id);
|
|
575
669
|
};
|
|
576
|
-
}, [interval2, tz]);
|
|
577
|
-
const billingMs = cfg.billingInterval * 6e4;
|
|
578
|
-
useEffect(() => {
|
|
579
|
-
let active = true;
|
|
580
|
-
const load = () => fetchBilling().then((b) => {
|
|
581
|
-
if (active) setBilling(b);
|
|
582
|
-
}).catch(() => {
|
|
583
|
-
});
|
|
584
|
-
load();
|
|
585
|
-
const id = setInterval(load, billingMs);
|
|
586
|
-
return () => {
|
|
587
|
-
active = false;
|
|
588
|
-
clearInterval(id);
|
|
589
|
-
};
|
|
590
|
-
}, [billingMs]);
|
|
670
|
+
}, [interval2, tz, config2, activeSlot.id]);
|
|
591
671
|
useEffect(() => {
|
|
592
672
|
tableLoadedOnce.current = false;
|
|
593
673
|
setTable(null);
|
|
594
|
-
}, [tz]);
|
|
674
|
+
}, [tz, activeSlot.id]);
|
|
595
675
|
useEffect(() => {
|
|
596
676
|
if (tab !== 1) return;
|
|
597
677
|
if (tableLoadedOnce.current && table) return;
|
|
598
678
|
let active = true;
|
|
599
679
|
setTableLoading(true);
|
|
600
|
-
fetchTable(tz).then((result) => {
|
|
680
|
+
fetchTable(tz, activeSlot.id === null ? void 0 : activeSlot.homeDir).then((result) => {
|
|
601
681
|
if (active) {
|
|
602
682
|
setTable(result);
|
|
603
683
|
setTableLoading(false);
|
|
@@ -609,13 +689,13 @@ function App({ interval: cliInterval }) {
|
|
|
609
689
|
return () => {
|
|
610
690
|
active = false;
|
|
611
691
|
};
|
|
612
|
-
}, [tab, tz]);
|
|
692
|
+
}, [tab, tz, activeSlot.id]);
|
|
613
693
|
useEffect(() => {
|
|
614
694
|
if (tab !== 1 || !tableLoadedOnce.current) return;
|
|
615
695
|
let active = true;
|
|
616
696
|
const id = setInterval(async () => {
|
|
617
697
|
try {
|
|
618
|
-
const result = await fetchTable(tz);
|
|
698
|
+
const result = await fetchTable(tz, activeSlot.id === null ? void 0 : activeSlot.homeDir);
|
|
619
699
|
if (active) setTable(result);
|
|
620
700
|
} catch {
|
|
621
701
|
}
|
|
@@ -624,7 +704,7 @@ function App({ interval: cliInterval }) {
|
|
|
624
704
|
active = false;
|
|
625
705
|
clearInterval(id);
|
|
626
706
|
};
|
|
627
|
-
}, [tab, interval2, tz]);
|
|
707
|
+
}, [tab, interval2, tz, activeSlot.id]);
|
|
628
708
|
const resetView = useCallback(() => {
|
|
629
709
|
setCursor(0);
|
|
630
710
|
setExpanded(-1);
|
|
@@ -643,7 +723,156 @@ function App({ interval: cliInterval }) {
|
|
|
643
723
|
mouse.events.off("scroll", onScroll);
|
|
644
724
|
};
|
|
645
725
|
}, [tab]);
|
|
726
|
+
function updateConfig(fn) {
|
|
727
|
+
setConfig((prev) => {
|
|
728
|
+
const next = fn(prev ?? DEFAULT_CONFIG);
|
|
729
|
+
saveConfig(next);
|
|
730
|
+
return next;
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
function cycleAccount(dir) {
|
|
734
|
+
if (slots.length <= 1) return;
|
|
735
|
+
const next = (activeSlotIdx + dir + slots.length) % slots.length;
|
|
736
|
+
const targetId = slots[next].id;
|
|
737
|
+
updateConfig((c) => ({ ...c, activeAccountId: targetId }));
|
|
738
|
+
resetView();
|
|
739
|
+
}
|
|
740
|
+
function openAddAccount() {
|
|
741
|
+
setAccountForm({
|
|
742
|
+
mode: "add",
|
|
743
|
+
field: "name",
|
|
744
|
+
name: "",
|
|
745
|
+
homeDir: "~",
|
|
746
|
+
color: pickAccentColor(cfg.accounts),
|
|
747
|
+
editingId: null,
|
|
748
|
+
error: null
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
function openEditAccount(acc) {
|
|
752
|
+
setAccountForm({
|
|
753
|
+
mode: "edit",
|
|
754
|
+
field: "name",
|
|
755
|
+
name: acc.name,
|
|
756
|
+
homeDir: acc.homeDir,
|
|
757
|
+
color: acc.color || "cyan",
|
|
758
|
+
editingId: acc.id,
|
|
759
|
+
error: null
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
function commitAccountForm() {
|
|
763
|
+
if (!accountForm) return;
|
|
764
|
+
const name = accountForm.name.trim();
|
|
765
|
+
const homeDir = accountForm.homeDir.trim() || "~";
|
|
766
|
+
const color = accountForm.color;
|
|
767
|
+
if (!name) {
|
|
768
|
+
setAccountForm({ ...accountForm, error: "Name required", field: "name" });
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
updateConfig((c) => {
|
|
772
|
+
if (accountForm.mode === "add") {
|
|
773
|
+
const id = generateAccountId(name, c.accounts);
|
|
774
|
+
const account = { id, name, homeDir, color };
|
|
775
|
+
return {
|
|
776
|
+
...c,
|
|
777
|
+
accounts: [...c.accounts, account],
|
|
778
|
+
activeAccountId: c.accounts.length === 0 ? id : c.activeAccountId
|
|
779
|
+
};
|
|
780
|
+
} else {
|
|
781
|
+
return {
|
|
782
|
+
...c,
|
|
783
|
+
accounts: c.accounts.map(
|
|
784
|
+
(a) => a.id === accountForm.editingId ? { ...a, name, homeDir, color } : a
|
|
785
|
+
)
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
setAccountForm(null);
|
|
790
|
+
}
|
|
791
|
+
function cycleFormField(dir) {
|
|
792
|
+
const order = ["name", "homeDir", "color"];
|
|
793
|
+
setAccountForm((f) => {
|
|
794
|
+
if (!f) return f;
|
|
795
|
+
const i = order.indexOf(f.field);
|
|
796
|
+
const next = order[(i + dir + order.length) % order.length];
|
|
797
|
+
return { ...f, field: next };
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
function cycleColor(dir) {
|
|
801
|
+
setAccountForm((f) => {
|
|
802
|
+
if (!f) return f;
|
|
803
|
+
const i = COLOR_PALETTE.indexOf(f.color);
|
|
804
|
+
const idx = i < 0 ? 0 : i;
|
|
805
|
+
const next = COLOR_PALETTE[(idx + dir + COLOR_PALETTE.length) % COLOR_PALETTE.length];
|
|
806
|
+
return { ...f, color: next };
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
function deleteAccount(id) {
|
|
810
|
+
updateConfig((c) => ({
|
|
811
|
+
...c,
|
|
812
|
+
accounts: c.accounts.filter((a) => a.id !== id),
|
|
813
|
+
activeAccountId: c.activeAccountId === id ? null : c.activeAccountId
|
|
814
|
+
}));
|
|
815
|
+
}
|
|
816
|
+
const accountRowsStart = GENERAL_ROWS;
|
|
817
|
+
const totalSettingsRows = GENERAL_ROWS + cfg.accounts.length + 1;
|
|
646
818
|
useInput((input, key) => {
|
|
819
|
+
if (showSettings && accountForm) {
|
|
820
|
+
if (key.escape) {
|
|
821
|
+
setAccountForm(null);
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
if (key.tab) {
|
|
825
|
+
cycleFormField(key.shift ? -1 : 1);
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (key.upArrow) {
|
|
829
|
+
cycleFormField(-1);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
if (key.downArrow) {
|
|
833
|
+
cycleFormField(1);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
if (accountForm.field === "color") {
|
|
837
|
+
if (key.leftArrow) {
|
|
838
|
+
cycleColor(-1);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
if (key.rightArrow) {
|
|
842
|
+
cycleColor(1);
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
if (key.return) {
|
|
846
|
+
commitAccountForm();
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (key.return) {
|
|
852
|
+
if (accountForm.field === "name") {
|
|
853
|
+
setAccountForm((f) => f && { ...f, field: "homeDir" });
|
|
854
|
+
} else if (accountForm.field === "homeDir") {
|
|
855
|
+
setAccountForm((f) => f && { ...f, field: "color" });
|
|
856
|
+
}
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (key.backspace || key.delete) {
|
|
860
|
+
setAccountForm((f) => {
|
|
861
|
+
if (!f || f.field === "color") return f;
|
|
862
|
+
const cur = f[f.field];
|
|
863
|
+
return { ...f, [f.field]: cur.slice(0, -1), error: null };
|
|
864
|
+
});
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (input && !key.ctrl && !key.meta) {
|
|
868
|
+
setAccountForm((f) => {
|
|
869
|
+
if (!f || f.field === "color") return f;
|
|
870
|
+
return { ...f, [f.field]: f[f.field] + input, error: null };
|
|
871
|
+
});
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
647
876
|
if (showSettings && tzEdit !== null) {
|
|
648
877
|
if (key.escape) {
|
|
649
878
|
setTzEdit(null);
|
|
@@ -682,19 +911,31 @@ function App({ interval: cliInterval }) {
|
|
|
682
911
|
return;
|
|
683
912
|
}
|
|
684
913
|
if (showSettings) {
|
|
685
|
-
if (key.escape || input === "s")
|
|
686
|
-
|
|
687
|
-
|
|
914
|
+
if (key.escape || input === "s") {
|
|
915
|
+
setShowSettings(false);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
if (key.upArrow) {
|
|
919
|
+
setSettingsCursor((c) => Math.max(0, c - 1));
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
if (key.downArrow) {
|
|
923
|
+
setSettingsCursor((c) => Math.min(totalSettingsRows - 1, c + 1));
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
688
926
|
if (settingsCursor === 0) {
|
|
689
927
|
if (key.leftArrow) updateConfig((c) => ({ ...c, interval: Math.max(1, c.interval - 1) }));
|
|
690
928
|
if (key.rightArrow) updateConfig((c) => ({ ...c, interval: c.interval + 1 }));
|
|
929
|
+
return;
|
|
691
930
|
}
|
|
692
931
|
if (settingsCursor === 1) {
|
|
693
932
|
if (key.leftArrow) updateConfig((c) => ({ ...c, billingInterval: Math.max(1, c.billingInterval - 1) }));
|
|
694
933
|
if (key.rightArrow) updateConfig((c) => ({ ...c, billingInterval: c.billingInterval + 1 }));
|
|
934
|
+
return;
|
|
695
935
|
}
|
|
696
936
|
if (settingsCursor === 2 && (key.leftArrow || key.rightArrow || key.return)) {
|
|
697
937
|
updateConfig((c) => ({ ...c, clearScreen: !c.clearScreen }));
|
|
938
|
+
return;
|
|
698
939
|
}
|
|
699
940
|
if (settingsCursor === 3) {
|
|
700
941
|
if (key.return) {
|
|
@@ -704,28 +945,57 @@ function App({ interval: cliInterval }) {
|
|
|
704
945
|
if (key.leftArrow || key.rightArrow) {
|
|
705
946
|
updateConfig((c) => ({ ...c, timezone: c.timezone === null ? systemTimezone() : null }));
|
|
706
947
|
}
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
const accIdx = settingsCursor - accountRowsStart;
|
|
951
|
+
if (accIdx >= 0 && accIdx < cfg.accounts.length) {
|
|
952
|
+
const acc = cfg.accounts[accIdx];
|
|
953
|
+
if (key.return) {
|
|
954
|
+
openEditAccount(acc);
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
if (input === "d" || input === "x") {
|
|
958
|
+
deleteAccount(acc.id);
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
if (input === " ") {
|
|
962
|
+
updateConfig((c) => ({ ...c, activeAccountId: acc.id }));
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
if (accIdx === cfg.accounts.length && key.return) {
|
|
968
|
+
openAddAccount();
|
|
969
|
+
return;
|
|
707
970
|
}
|
|
708
971
|
return;
|
|
709
972
|
}
|
|
710
973
|
if (input === "s") {
|
|
711
974
|
setShowSettings(true);
|
|
975
|
+
setSettingsCursor(0);
|
|
712
976
|
return;
|
|
713
977
|
}
|
|
714
|
-
if (
|
|
715
|
-
|
|
716
|
-
resetView();
|
|
978
|
+
if (input === "a") {
|
|
979
|
+
cycleAccount(1);
|
|
717
980
|
return;
|
|
718
981
|
}
|
|
719
|
-
if (input === "
|
|
720
|
-
|
|
721
|
-
resetView();
|
|
982
|
+
if (input === "A") {
|
|
983
|
+
cycleAccount(-1);
|
|
722
984
|
return;
|
|
723
985
|
}
|
|
724
|
-
if (
|
|
725
|
-
setTab(1);
|
|
986
|
+
if (key.tab) {
|
|
987
|
+
setTab((t) => (t + 1) % TABS.length);
|
|
726
988
|
resetView();
|
|
727
989
|
return;
|
|
728
990
|
}
|
|
991
|
+
if (input && /^[0-9]$/.test(input) && slots.length > 1) {
|
|
992
|
+
const target = slots[parseInt(input, 10)];
|
|
993
|
+
if (target) {
|
|
994
|
+
updateConfig((c) => ({ ...c, activeAccountId: target.id }));
|
|
995
|
+
resetView();
|
|
996
|
+
}
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
729
999
|
if (tab === 1) {
|
|
730
1000
|
if (input === "d") {
|
|
731
1001
|
setView(0);
|
|
@@ -789,15 +1059,11 @@ function App({ interval: cliInterval }) {
|
|
|
789
1059
|
return;
|
|
790
1060
|
}
|
|
791
1061
|
}, { isActive: IS_TTY });
|
|
792
|
-
function updateConfig(fn) {
|
|
793
|
-
setConfig((prev) => {
|
|
794
|
-
const next = fn(prev ?? DEFAULT_CONFIG);
|
|
795
|
-
saveConfig(next);
|
|
796
|
-
return next;
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
1062
|
if (error) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { color: "red", children: error }) });
|
|
800
|
-
if (!
|
|
1063
|
+
if (!config2) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Loading..." }) });
|
|
1064
|
+
const firstStats = stats.size > 0 ? [...stats.values()][0] : null;
|
|
1065
|
+
if (!firstStats?.dashboard) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Loading..." }) });
|
|
1066
|
+
const peakBilling = firstStats.billing;
|
|
801
1067
|
const rawTableData = table ? [table.daily, table.weekly, table.monthly][view] : [];
|
|
802
1068
|
const tableData = sortRows(rawTableData, sort);
|
|
803
1069
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, height: rows, children: [
|
|
@@ -809,20 +1075,47 @@ function App({ interval: cliInterval }) {
|
|
|
809
1075
|
] }),
|
|
810
1076
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
811
1077
|
" \xB7 every ",
|
|
812
|
-
|
|
1078
|
+
cfg.interval,
|
|
813
1079
|
"s"
|
|
814
1080
|
] })
|
|
815
1081
|
] }),
|
|
816
1082
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
817
|
-
|
|
818
|
-
/* @__PURE__ */ jsx(PeakBadge, { peak:
|
|
1083
|
+
peakBilling?.peak && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1084
|
+
/* @__PURE__ */ jsx(PeakBadge, { peak: peakBilling.peak }),
|
|
819
1085
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " })
|
|
820
1086
|
] }),
|
|
821
1087
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: time(updated, tz) })
|
|
822
1088
|
] })
|
|
823
1089
|
] }),
|
|
824
|
-
showSettings ? /* @__PURE__ */ jsx(
|
|
825
|
-
|
|
1090
|
+
showSettings ? /* @__PURE__ */ jsx(
|
|
1091
|
+
SettingsView,
|
|
1092
|
+
{
|
|
1093
|
+
config: cfg,
|
|
1094
|
+
cursor: settingsCursor,
|
|
1095
|
+
tzEdit,
|
|
1096
|
+
tzError,
|
|
1097
|
+
resolvedTz: tz,
|
|
1098
|
+
accountForm,
|
|
1099
|
+
onSettingsClick: (i) => setSettingsCursor(i),
|
|
1100
|
+
onAddAccount: openAddAccount,
|
|
1101
|
+
onEditAccount: openEditAccount,
|
|
1102
|
+
onActivateAccount: (id) => updateConfig((c) => ({ ...c, activeAccountId: id })),
|
|
1103
|
+
activeAccountId: cfg.activeAccountId
|
|
1104
|
+
}
|
|
1105
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1106
|
+
slots.length > 1 && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(
|
|
1107
|
+
AccountStrip,
|
|
1108
|
+
{
|
|
1109
|
+
slots,
|
|
1110
|
+
activeIdx: activeSlotIdx,
|
|
1111
|
+
onSelect: (i) => {
|
|
1112
|
+
const id = slots[i].id;
|
|
1113
|
+
updateConfig((c) => ({ ...c, activeAccountId: id }));
|
|
1114
|
+
resetView();
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
) }),
|
|
1118
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: slots.length > 1 ? 0 : 1, children: [
|
|
826
1119
|
/* @__PURE__ */ jsx(TabBar, { tabs: TABS, active: tab, onSelect: (i) => {
|
|
827
1120
|
setTab(i);
|
|
828
1121
|
resetView();
|
|
@@ -830,7 +1123,14 @@ function App({ interval: cliInterval }) {
|
|
|
830
1123
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " Tab/\u2190\u2192" })
|
|
831
1124
|
] }),
|
|
832
1125
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
833
|
-
tab === 0 && /* @__PURE__ */ jsx(
|
|
1126
|
+
tab === 0 && /* @__PURE__ */ jsx(
|
|
1127
|
+
DashboardView,
|
|
1128
|
+
{
|
|
1129
|
+
slots: visibleSlots,
|
|
1130
|
+
stats,
|
|
1131
|
+
compact: visibleSlots.length > 1
|
|
1132
|
+
}
|
|
1133
|
+
),
|
|
834
1134
|
tab === 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
835
1135
|
/* @__PURE__ */ jsx(ViewBar, { views: VIEWS, active: view, sort: SORTS[sort], onSelect: (i) => {
|
|
836
1136
|
setView(i);
|
|
@@ -843,7 +1143,7 @@ function App({ interval: cliInterval }) {
|
|
|
843
1143
|
rows: tableData,
|
|
844
1144
|
cursor,
|
|
845
1145
|
expanded,
|
|
846
|
-
maxRows: rows -
|
|
1146
|
+
maxRows: rows - 14,
|
|
847
1147
|
cols,
|
|
848
1148
|
onRowClick: (idx) => {
|
|
849
1149
|
if (idx === cursor) setExpanded((e) => e === idx ? -1 : idx);
|
|
@@ -853,16 +1153,18 @@ function App({ interval: cliInterval }) {
|
|
|
853
1153
|
)
|
|
854
1154
|
] })
|
|
855
1155
|
] }),
|
|
856
|
-
(tab === 0 || showSettings) && /* @__PURE__ */ jsx(Footer, {})
|
|
1156
|
+
(tab === 0 || showSettings) && /* @__PURE__ */ jsx(Footer, { hasAccounts: slots.length > 1 })
|
|
857
1157
|
] });
|
|
858
1158
|
}
|
|
859
|
-
function Footer() {
|
|
1159
|
+
function Footer({ hasAccounts }) {
|
|
860
1160
|
return /* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
861
1161
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "by " }),
|
|
862
1162
|
/* @__PURE__ */ jsx(Text, { children: "David Ilie" }),
|
|
863
1163
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " (" }),
|
|
864
1164
|
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "davidilie.com" }),
|
|
865
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ") \xB7 s=settings
|
|
1165
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ") \xB7 s=settings " }),
|
|
1166
|
+
hasAccounts && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "0-9=jump a/A=cycle " }),
|
|
1167
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "q=quit" })
|
|
866
1168
|
] });
|
|
867
1169
|
}
|
|
868
1170
|
function TabBar({ tabs, active, onSelect }) {
|
|
@@ -876,6 +1178,19 @@ function TabBar({ tabs, active, onSelect }) {
|
|
|
876
1178
|
" "
|
|
877
1179
|
] }) }, t)) });
|
|
878
1180
|
}
|
|
1181
|
+
function AccountStrip({ slots, activeIdx, onSelect }) {
|
|
1182
|
+
return /* @__PURE__ */ jsx(Box, { flexWrap: "wrap", children: slots.map((s, i) => {
|
|
1183
|
+
const active = i === activeIdx;
|
|
1184
|
+
const dot = s.id === null ? "\u2726" : "\u25CF";
|
|
1185
|
+
return /* @__PURE__ */ jsxs(ClickableBox, { onClick: () => onSelect(i), marginRight: 2, children: [
|
|
1186
|
+
/* @__PURE__ */ jsx(Text, { dimColor: !active, children: i }),
|
|
1187
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1188
|
+
/* @__PURE__ */ jsx(Text, { color: s.color, bold: active, dimColor: !active, children: dot }),
|
|
1189
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1190
|
+
active ? /* @__PURE__ */ jsx(Text, { bold: true, color: s.color, children: s.name }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: s.name })
|
|
1191
|
+
] }, s.id ?? "__all__");
|
|
1192
|
+
}) });
|
|
1193
|
+
}
|
|
879
1194
|
function ViewBar({ views, active, sort, onSelect }) {
|
|
880
1195
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
881
1196
|
views.map((v, i) => /* @__PURE__ */ jsx(ClickableBox, { onClick: () => onSelect(i), marginRight: 2, children: i === active ? /* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
@@ -904,13 +1219,40 @@ function sortRows(rows, sortIdx) {
|
|
|
904
1219
|
return sorted;
|
|
905
1220
|
}
|
|
906
1221
|
}
|
|
907
|
-
|
|
908
|
-
|
|
1222
|
+
var COLOR_PALETTE = [
|
|
1223
|
+
"cyan",
|
|
1224
|
+
"magenta",
|
|
1225
|
+
"green",
|
|
1226
|
+
"yellow",
|
|
1227
|
+
"blue",
|
|
1228
|
+
"red",
|
|
1229
|
+
"cyanBright",
|
|
1230
|
+
"magentaBright",
|
|
1231
|
+
"greenBright"
|
|
1232
|
+
];
|
|
1233
|
+
function SettingsView({
|
|
1234
|
+
config: config2,
|
|
1235
|
+
cursor,
|
|
1236
|
+
tzEdit,
|
|
1237
|
+
tzError,
|
|
1238
|
+
resolvedTz,
|
|
1239
|
+
accountForm,
|
|
1240
|
+
onAddAccount,
|
|
1241
|
+
onEditAccount,
|
|
1242
|
+
onActivateAccount,
|
|
1243
|
+
activeAccountId
|
|
1244
|
+
}) {
|
|
1245
|
+
const editingTz = tzEdit !== null;
|
|
909
1246
|
const tzDisplay = config2.timezone === null ? `System (${resolvedTz})` : config2.timezone;
|
|
1247
|
+
const accountRowsStart = GENERAL_ROWS;
|
|
1248
|
+
if (accountForm) {
|
|
1249
|
+
return /* @__PURE__ */ jsx(AccountFormView, { form: accountForm, accounts: config2.accounts });
|
|
1250
|
+
}
|
|
910
1251
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
911
1252
|
/* @__PURE__ */ jsx(Text, { bold: true, children: "Settings" }),
|
|
912
1253
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: configLocation() }),
|
|
913
1254
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1255
|
+
/* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: "General" }),
|
|
914
1256
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
915
1257
|
/* @__PURE__ */ jsxs(Text, { color: cursor === 0 ? "green" : void 0, children: [
|
|
916
1258
|
cursor === 0 ? "\u25B8" : " ",
|
|
@@ -963,7 +1305,7 @@ function SettingsView({ config: config2, cursor, tzEdit, tzError, resolvedTz })
|
|
|
963
1305
|
" "
|
|
964
1306
|
] }),
|
|
965
1307
|
/* @__PURE__ */ jsx(Text, { children: "Timezone " }),
|
|
966
|
-
|
|
1308
|
+
editingTz ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
967
1309
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "[" }),
|
|
968
1310
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: tzEdit }),
|
|
969
1311
|
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "_" }),
|
|
@@ -975,32 +1317,278 @@ function SettingsView({ config: config2, cursor, tzEdit, tzError, resolvedTz })
|
|
|
975
1317
|
tzError
|
|
976
1318
|
] }),
|
|
977
1319
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
978
|
-
|
|
1320
|
+
/* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: "Claude accounts" }),
|
|
1321
|
+
config2.accounts.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " none \u2014 using default Claude HOME" }),
|
|
1322
|
+
config2.accounts.map((acc, i) => {
|
|
1323
|
+
const idx = accountRowsStart + i;
|
|
1324
|
+
const selected = cursor === idx;
|
|
1325
|
+
const isActive = acc.id === activeAccountId;
|
|
1326
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1327
|
+
/* @__PURE__ */ jsxs(Text, { color: selected ? "green" : void 0, children: [
|
|
1328
|
+
selected ? "\u25B8" : " ",
|
|
1329
|
+
" "
|
|
1330
|
+
] }),
|
|
1331
|
+
/* @__PURE__ */ jsxs(Text, { color: acc.color || "cyan", children: [
|
|
1332
|
+
isActive ? "\u25CF" : "\u25CB",
|
|
1333
|
+
" "
|
|
1334
|
+
] }),
|
|
1335
|
+
/* @__PURE__ */ jsx(Box, { width: 16, children: /* @__PURE__ */ jsx(Text, { bold: true, children: acc.name }) }),
|
|
1336
|
+
/* @__PURE__ */ jsx(Box, { width: 14, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: acc.id }) }),
|
|
1337
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: acc.homeDir })
|
|
1338
|
+
] }, acc.id);
|
|
1339
|
+
}),
|
|
1340
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1341
|
+
/* @__PURE__ */ jsxs(Text, { color: cursor === accountRowsStart + config2.accounts.length ? "green" : void 0, children: [
|
|
1342
|
+
cursor === accountRowsStart + config2.accounts.length ? "\u25B8" : " ",
|
|
1343
|
+
" "
|
|
1344
|
+
] }),
|
|
1345
|
+
/* @__PURE__ */ jsx(Text, { color: "greenBright", children: "+ " }),
|
|
1346
|
+
/* @__PURE__ */ jsx(Text, { children: "Add account" })
|
|
1347
|
+
] }),
|
|
1348
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1349
|
+
editingTz ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "type IANA name (e.g. Europe/London) \xB7 empty = System \xB7 Enter save \xB7 Esc cancel" }) : cursor >= accountRowsStart && cursor < accountRowsStart + config2.accounts.length ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191\u2193 select \xB7 Enter edit \xB7 space activate \xB7 d delete \xB7 s/Esc close" }) : cursor === accountRowsStart + config2.accounts.length ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191\u2193 select \xB7 Enter add account \xB7 s/Esc close" }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191\u2193 select \u2190\u2192 adjust Enter edit s/Esc close" })
|
|
979
1350
|
] });
|
|
980
1351
|
}
|
|
981
|
-
function
|
|
982
|
-
|
|
1352
|
+
function AccountFormView({ form, accounts }) {
|
|
1353
|
+
const previewId = form.mode === "add" ? generateAccountId(form.name || "account", accounts) : form.editingId ?? "";
|
|
1354
|
+
const accent = form.color;
|
|
1355
|
+
const stepIndex = { name: 1, homeDir: 2, color: 3 };
|
|
1356
|
+
const step = stepIndex[form.field];
|
|
1357
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
1358
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1359
|
+
/* @__PURE__ */ jsx(Text, { color: accent, bold: true, children: "\u258D" }),
|
|
1360
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
1361
|
+
" ",
|
|
1362
|
+
form.mode === "add" ? "NEW ACCOUNT" : "EDIT ACCOUNT"
|
|
1363
|
+
] }),
|
|
1364
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1365
|
+
" step ",
|
|
1366
|
+
step,
|
|
1367
|
+
" of 3"
|
|
1368
|
+
] })
|
|
1369
|
+
] }),
|
|
1370
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Stepper, { active: form.field, accent }) }),
|
|
983
1371
|
/* @__PURE__ */ jsxs(
|
|
984
1372
|
Box,
|
|
985
1373
|
{
|
|
1374
|
+
marginTop: 1,
|
|
986
1375
|
flexDirection: "column",
|
|
1376
|
+
borderStyle: "round",
|
|
1377
|
+
borderColor: accent,
|
|
1378
|
+
paddingX: 2,
|
|
1379
|
+
paddingY: 1,
|
|
1380
|
+
children: [
|
|
1381
|
+
/* @__PURE__ */ jsx(
|
|
1382
|
+
FormField,
|
|
1383
|
+
{
|
|
1384
|
+
label: "Name",
|
|
1385
|
+
hint: "display name for this Claude account",
|
|
1386
|
+
value: form.name,
|
|
1387
|
+
focused: form.field === "name",
|
|
1388
|
+
accent,
|
|
1389
|
+
placeholder: "e.g. Work, Personal"
|
|
1390
|
+
}
|
|
1391
|
+
),
|
|
1392
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1393
|
+
/* @__PURE__ */ jsx(
|
|
1394
|
+
FormField,
|
|
1395
|
+
{
|
|
1396
|
+
label: "Home directory",
|
|
1397
|
+
hint: "path containing .claude/ \xB7 ~ for default",
|
|
1398
|
+
value: form.homeDir,
|
|
1399
|
+
focused: form.field === "homeDir",
|
|
1400
|
+
accent,
|
|
1401
|
+
placeholder: "~/claude-work",
|
|
1402
|
+
mono: true
|
|
1403
|
+
}
|
|
1404
|
+
),
|
|
1405
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1406
|
+
/* @__PURE__ */ jsx(
|
|
1407
|
+
ColorField,
|
|
1408
|
+
{
|
|
1409
|
+
value: form.color,
|
|
1410
|
+
focused: form.field === "color"
|
|
1411
|
+
}
|
|
1412
|
+
),
|
|
1413
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1414
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1415
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "id " }),
|
|
1416
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2524 " }),
|
|
1417
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: accent, children: previewId || "account" }),
|
|
1418
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u251C" }),
|
|
1419
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " auto-generated from name" })
|
|
1420
|
+
] })
|
|
1421
|
+
]
|
|
1422
|
+
}
|
|
1423
|
+
),
|
|
1424
|
+
form.error && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
1425
|
+
"\u26A0 ",
|
|
1426
|
+
form.error
|
|
1427
|
+
] }) }),
|
|
1428
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
1429
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "tab/\u2191\u2193 " }),
|
|
1430
|
+
/* @__PURE__ */ jsx(Text, { children: "switch field" }),
|
|
1431
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
|
|
1432
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "enter " }),
|
|
1433
|
+
/* @__PURE__ */ jsx(Text, { children: form.field === "color" ? "save" : "next" }),
|
|
1434
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
|
|
1435
|
+
form.field === "color" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1436
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2190\u2192 " }),
|
|
1437
|
+
/* @__PURE__ */ jsx(Text, { children: "pick color" }),
|
|
1438
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " })
|
|
1439
|
+
] }),
|
|
1440
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "esc " }),
|
|
1441
|
+
/* @__PURE__ */ jsx(Text, { children: "cancel" })
|
|
1442
|
+
] })
|
|
1443
|
+
] });
|
|
1444
|
+
}
|
|
1445
|
+
function Stepper({ active, accent }) {
|
|
1446
|
+
const steps = [
|
|
1447
|
+
{ id: "name", label: "Name" },
|
|
1448
|
+
{ id: "homeDir", label: "Home" },
|
|
1449
|
+
{ id: "color", label: "Color" }
|
|
1450
|
+
];
|
|
1451
|
+
const order = steps.map((s) => s.id);
|
|
1452
|
+
const activeIdx = order.indexOf(active);
|
|
1453
|
+
return /* @__PURE__ */ jsx(Box, { children: steps.map((s, i) => {
|
|
1454
|
+
const done = i < activeIdx;
|
|
1455
|
+
const cur = i === activeIdx;
|
|
1456
|
+
const dot = done ? "\u25CF" : cur ? "\u25C9" : "\u25CB";
|
|
1457
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1458
|
+
/* @__PURE__ */ jsxs(Text, { color: cur ? accent : done ? accent : void 0, dimColor: !cur && !done, children: [
|
|
1459
|
+
dot,
|
|
1460
|
+
" "
|
|
1461
|
+
] }),
|
|
1462
|
+
/* @__PURE__ */ jsx(Text, { bold: cur, color: cur ? accent : void 0, dimColor: !cur, children: s.label }),
|
|
1463
|
+
i < steps.length - 1 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2500 " })
|
|
1464
|
+
] }, s.id);
|
|
1465
|
+
}) });
|
|
1466
|
+
}
|
|
1467
|
+
function FormField({
|
|
1468
|
+
label,
|
|
1469
|
+
hint,
|
|
1470
|
+
value,
|
|
1471
|
+
focused,
|
|
1472
|
+
accent,
|
|
1473
|
+
placeholder,
|
|
1474
|
+
mono
|
|
1475
|
+
}) {
|
|
1476
|
+
const display = value === "" ? placeholder : value;
|
|
1477
|
+
const isPlaceholder = value === "";
|
|
1478
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1479
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: focused ? accent : void 0, bold: focused, dimColor: !focused, children: [
|
|
1480
|
+
focused ? "\u25B8" : " ",
|
|
1481
|
+
" ",
|
|
1482
|
+
label
|
|
1483
|
+
] }) }),
|
|
1484
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 0, children: [
|
|
1485
|
+
/* @__PURE__ */ jsxs(Text, { color: focused ? accent : void 0, children: [
|
|
1486
|
+
" ",
|
|
1487
|
+
focused ? "\u258C" : " ",
|
|
1488
|
+
" "
|
|
1489
|
+
] }),
|
|
1490
|
+
/* @__PURE__ */ jsx(
|
|
1491
|
+
Text,
|
|
1492
|
+
{
|
|
1493
|
+
bold: focused && !isPlaceholder,
|
|
1494
|
+
color: focused && !isPlaceholder ? accent : void 0,
|
|
1495
|
+
dimColor: isPlaceholder,
|
|
1496
|
+
italic: mono && isPlaceholder,
|
|
1497
|
+
children: display
|
|
1498
|
+
}
|
|
1499
|
+
),
|
|
1500
|
+
focused && /* @__PURE__ */ jsx(Text, { color: accent, children: "\u258F" })
|
|
1501
|
+
] }),
|
|
1502
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1503
|
+
" ",
|
|
1504
|
+
hint
|
|
1505
|
+
] }) })
|
|
1506
|
+
] });
|
|
1507
|
+
}
|
|
1508
|
+
function ColorField({ value, focused }) {
|
|
1509
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1510
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: focused ? value : void 0, bold: focused, dimColor: !focused, children: [
|
|
1511
|
+
focused ? "\u25B8" : " ",
|
|
1512
|
+
" Accent color"
|
|
1513
|
+
] }) }),
|
|
1514
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 0, children: [
|
|
1515
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1516
|
+
" ",
|
|
1517
|
+
focused ? "\u258C" : " ",
|
|
1518
|
+
" "
|
|
1519
|
+
] }),
|
|
1520
|
+
COLOR_PALETTE.map((c, i) => {
|
|
1521
|
+
const selected = c === value;
|
|
1522
|
+
return /* @__PURE__ */ jsx(Box, { marginRight: 1, children: selected ? /* @__PURE__ */ jsx(Text, { bold: true, color: c, children: "[\u25CF]" }) : /* @__PURE__ */ jsx(Text, { color: c, dimColor: !focused, children: i === COLOR_PALETTE.length - 1 ? " \u25CF" : " \u25CF" }) }, c);
|
|
1523
|
+
})
|
|
1524
|
+
] }),
|
|
1525
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: " shows on dashboard, account strip, borders" }) })
|
|
1526
|
+
] });
|
|
1527
|
+
}
|
|
1528
|
+
function DashboardView({ slots, stats, compact }) {
|
|
1529
|
+
const slotKey = (s2) => s2.id ?? "__default__";
|
|
1530
|
+
if (compact) {
|
|
1531
|
+
const accountStats = slots.map((slot2) => ({ slot: slot2, s: stats.get(slotKey(slot2)) })).filter((x) => !!x.s?.dashboard);
|
|
1532
|
+
return /* @__PURE__ */ jsx(ComparisonView, { accountStats });
|
|
1533
|
+
}
|
|
1534
|
+
const slot = slots[0];
|
|
1535
|
+
const s = stats.get(slotKey(slot));
|
|
1536
|
+
if (!s?.dashboard) {
|
|
1537
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1538
|
+
/* @__PURE__ */ jsxs(Text, { color: slot.color, bold: true, children: [
|
|
1539
|
+
"\u25CF ",
|
|
1540
|
+
slot.name,
|
|
1541
|
+
" "
|
|
1542
|
+
] }),
|
|
1543
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "loading..." })
|
|
1544
|
+
] });
|
|
1545
|
+
}
|
|
1546
|
+
return /* @__PURE__ */ jsx(SoloAccountCard, { slot, dashboard: s.dashboard, billing: s.billing });
|
|
1547
|
+
}
|
|
1548
|
+
function bar(value, max, width) {
|
|
1549
|
+
if (max <= 0) return { filled: 0, empty: width };
|
|
1550
|
+
const filled = Math.max(0, Math.min(width, Math.round(value / max * width)));
|
|
1551
|
+
return { filled, empty: width - filled };
|
|
1552
|
+
}
|
|
1553
|
+
function SoloAccountCard({ slot, dashboard, billing }) {
|
|
1554
|
+
const maxCost = Math.max(dashboard.today.cost, dashboard.week.cost, dashboard.month.cost, 0.01);
|
|
1555
|
+
const maxTokens = Math.max(dashboard.today.tokens, dashboard.week.tokens, dashboard.month.tokens, 1);
|
|
1556
|
+
const rows = [
|
|
1557
|
+
{ label: "Today", s: dashboard.today },
|
|
1558
|
+
{ label: "This Week", s: dashboard.week },
|
|
1559
|
+
{ label: "This Month", s: dashboard.month }
|
|
1560
|
+
];
|
|
1561
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1562
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1563
|
+
/* @__PURE__ */ jsxs(Text, { color: slot.color, bold: true, children: [
|
|
1564
|
+
"\u25CF ",
|
|
1565
|
+
slot.name
|
|
1566
|
+
] }),
|
|
1567
|
+
slot.id && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1568
|
+
" ",
|
|
1569
|
+
slot.id
|
|
1570
|
+
] })
|
|
1571
|
+
] }),
|
|
1572
|
+
/* @__PURE__ */ jsxs(
|
|
1573
|
+
Box,
|
|
1574
|
+
{
|
|
1575
|
+
flexDirection: "column",
|
|
1576
|
+
marginTop: 1,
|
|
987
1577
|
paddingLeft: 1,
|
|
988
1578
|
borderStyle: "bold",
|
|
989
|
-
borderColor:
|
|
1579
|
+
borderColor: slot.color,
|
|
990
1580
|
borderRight: false,
|
|
991
1581
|
borderTop: false,
|
|
992
1582
|
borderBottom: false,
|
|
993
1583
|
children: [
|
|
994
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children: "
|
|
1584
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Usage" }),
|
|
995
1585
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
996
|
-
/* @__PURE__ */ jsx(
|
|
997
|
-
/* @__PURE__ */
|
|
998
|
-
/* @__PURE__ */ jsx(SummaryRow, { label: "This Month", summary: data.month }),
|
|
999
|
-
data.burnRate > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1586
|
+
rows.map((r) => /* @__PURE__ */ jsx(DualBarRow, { label: r.label, cost: r.s.cost, tokens: r.s.tokens, maxCost, maxTokens, color: slot.color }, r.label)),
|
|
1587
|
+
dashboard.burnRate > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1000
1588
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1001
1589
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1002
1590
|
/* @__PURE__ */ jsx(Box, { width: 14, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Burn rate" }) }),
|
|
1003
|
-
/* @__PURE__ */ jsx(Box, { width: 12, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text, { color: "red", children: currency(
|
|
1591
|
+
/* @__PURE__ */ jsx(Box, { width: 12, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text, { color: "red", children: currency(dashboard.burnRate) }) }),
|
|
1004
1592
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "/hr" })
|
|
1005
1593
|
] })
|
|
1006
1594
|
] })
|
|
@@ -1043,6 +1631,133 @@ function DashboardView({ data, billing }) {
|
|
|
1043
1631
|
)
|
|
1044
1632
|
] });
|
|
1045
1633
|
}
|
|
1634
|
+
function DualBarRow({
|
|
1635
|
+
label,
|
|
1636
|
+
cost,
|
|
1637
|
+
tokens: tokens2,
|
|
1638
|
+
maxCost,
|
|
1639
|
+
maxTokens,
|
|
1640
|
+
color
|
|
1641
|
+
}) {
|
|
1642
|
+
const W = 18;
|
|
1643
|
+
const c = bar(cost, maxCost, W);
|
|
1644
|
+
const t = bar(tokens2, maxTokens, W);
|
|
1645
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 0, children: [
|
|
1646
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1647
|
+
/* @__PURE__ */ jsx(Box, { width: 12, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: label }) }),
|
|
1648
|
+
/* @__PURE__ */ jsx(Text, { color, children: "\u2588".repeat(c.filled) }),
|
|
1649
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2591".repeat(c.empty) }),
|
|
1650
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1651
|
+
/* @__PURE__ */ jsx(Box, { width: 11, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: currency(cost) }) })
|
|
1652
|
+
] }),
|
|
1653
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1654
|
+
/* @__PURE__ */ jsx(Box, { width: 12, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
|
|
1655
|
+
/* @__PURE__ */ jsx(Text, { color, dimColor: true, children: "\u2593".repeat(t.filled) }),
|
|
1656
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2591".repeat(t.empty) }),
|
|
1657
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1658
|
+
/* @__PURE__ */ jsx(Box, { width: 11, justifyContent: "flex-end", children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1659
|
+
tokens(tokens2),
|
|
1660
|
+
" tk"
|
|
1661
|
+
] }) })
|
|
1662
|
+
] })
|
|
1663
|
+
] });
|
|
1664
|
+
}
|
|
1665
|
+
function ComparisonView({ accountStats }) {
|
|
1666
|
+
if (accountStats.length === 0) {
|
|
1667
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No accounts loaded yet..." });
|
|
1668
|
+
}
|
|
1669
|
+
const periods = [
|
|
1670
|
+
{ key: "Today", pick: (d) => d.today },
|
|
1671
|
+
{ key: "This Week", pick: (d) => d.week },
|
|
1672
|
+
{ key: "This Month", pick: (d) => d.month }
|
|
1673
|
+
];
|
|
1674
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1675
|
+
periods.map((p, i) => {
|
|
1676
|
+
const rows = accountStats.map(({ slot, s }) => ({
|
|
1677
|
+
slot,
|
|
1678
|
+
summary: p.pick(s.dashboard)
|
|
1679
|
+
}));
|
|
1680
|
+
const maxCost = Math.max(0.01, ...rows.map((r) => r.summary.cost));
|
|
1681
|
+
const maxTokens = Math.max(1, ...rows.map((r) => r.summary.tokens));
|
|
1682
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: i < periods.length - 1 ? 1 : 0, children: [
|
|
1683
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: p.key.toUpperCase() }) }),
|
|
1684
|
+
rows.map(({ slot, summary }) => /* @__PURE__ */ jsx(
|
|
1685
|
+
ComparisonRow,
|
|
1686
|
+
{
|
|
1687
|
+
slot,
|
|
1688
|
+
cost: summary.cost,
|
|
1689
|
+
tokens: summary.tokens,
|
|
1690
|
+
maxCost,
|
|
1691
|
+
maxTokens
|
|
1692
|
+
},
|
|
1693
|
+
slot.id ?? "__default__"
|
|
1694
|
+
))
|
|
1695
|
+
] }, p.key);
|
|
1696
|
+
}),
|
|
1697
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1698
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1699
|
+
/* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: "RATE LIMITS" }),
|
|
1700
|
+
accountStats.map(({ slot, s }) => /* @__PURE__ */ jsx(CompactLimitsRow, { slot, billing: s.billing }, slot.id ?? "__default__"))
|
|
1701
|
+
] })
|
|
1702
|
+
] });
|
|
1703
|
+
}
|
|
1704
|
+
function ComparisonRow({
|
|
1705
|
+
slot,
|
|
1706
|
+
cost,
|
|
1707
|
+
tokens: tokens2,
|
|
1708
|
+
maxCost,
|
|
1709
|
+
maxTokens
|
|
1710
|
+
}) {
|
|
1711
|
+
const W = 22;
|
|
1712
|
+
const c = bar(cost, maxCost, W);
|
|
1713
|
+
const t = bar(tokens2, maxTokens, W);
|
|
1714
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1715
|
+
/* @__PURE__ */ jsxs(Box, { width: 14, children: [
|
|
1716
|
+
/* @__PURE__ */ jsx(Text, { color: slot.color, children: "\u25CF " }),
|
|
1717
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: slot.name })
|
|
1718
|
+
] }),
|
|
1719
|
+
/* @__PURE__ */ jsx(Text, { color: slot.color, children: "\u2588".repeat(c.filled) }),
|
|
1720
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2591".repeat(c.empty) }),
|
|
1721
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1722
|
+
/* @__PURE__ */ jsx(Box, { width: 10, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: currency(cost) }) }),
|
|
1723
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1724
|
+
/* @__PURE__ */ jsx(Text, { color: slot.color, dimColor: true, children: "\u2593".repeat(t.filled) }),
|
|
1725
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2591".repeat(t.empty) }),
|
|
1726
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1727
|
+
/* @__PURE__ */ jsx(Box, { width: 10, justifyContent: "flex-end", children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1728
|
+
tokens(tokens2),
|
|
1729
|
+
" tk"
|
|
1730
|
+
] }) })
|
|
1731
|
+
] });
|
|
1732
|
+
}
|
|
1733
|
+
function CompactLimitsRow({ slot, billing }) {
|
|
1734
|
+
if (billing?.error) {
|
|
1735
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1736
|
+
/* @__PURE__ */ jsxs(Box, { width: 14, children: [
|
|
1737
|
+
/* @__PURE__ */ jsx(Text, { color: slot.color, children: "\u25CF " }),
|
|
1738
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: slot.name })
|
|
1739
|
+
] }),
|
|
1740
|
+
/* @__PURE__ */ jsx(Text, { color: "red", children: billing.error })
|
|
1741
|
+
] });
|
|
1742
|
+
}
|
|
1743
|
+
const fmtPct = (p) => p ? `${Math.round(p.utilization)}%` : "\u2014";
|
|
1744
|
+
const colorFor = (p) => {
|
|
1745
|
+
if (!p) return void 0;
|
|
1746
|
+
return p.utilization >= 80 ? "red" : p.utilization >= 50 ? "yellow" : "green";
|
|
1747
|
+
};
|
|
1748
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1749
|
+
/* @__PURE__ */ jsxs(Box, { width: 14, children: [
|
|
1750
|
+
/* @__PURE__ */ jsx(Text, { color: slot.color, children: "\u25CF " }),
|
|
1751
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: slot.name })
|
|
1752
|
+
] }),
|
|
1753
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "S " }),
|
|
1754
|
+
/* @__PURE__ */ jsx(Box, { width: 6, children: /* @__PURE__ */ jsx(Text, { bold: true, color: colorFor(billing?.session), children: fmtPct(billing?.session) }) }),
|
|
1755
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "W " }),
|
|
1756
|
+
/* @__PURE__ */ jsx(Box, { width: 6, children: /* @__PURE__ */ jsx(Text, { bold: true, color: colorFor(billing?.weekly), children: fmtPct(billing?.weekly) }) }),
|
|
1757
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Sonnet " }),
|
|
1758
|
+
/* @__PURE__ */ jsx(Box, { width: 6, children: /* @__PURE__ */ jsx(Text, { bold: true, color: colorFor(billing?.sonnet), children: fmtPct(billing?.sonnet) }) })
|
|
1759
|
+
] });
|
|
1760
|
+
}
|
|
1046
1761
|
function PeakBadge({ peak }) {
|
|
1047
1762
|
const color = peak.state === "peak" ? "red" : "green";
|
|
1048
1763
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
@@ -1080,16 +1795,6 @@ function LimitBar({ label, pct, resets }) {
|
|
|
1080
1795
|
] })
|
|
1081
1796
|
] });
|
|
1082
1797
|
}
|
|
1083
|
-
function SummaryRow({ label, summary }) {
|
|
1084
|
-
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1085
|
-
/* @__PURE__ */ jsx(Box, { width: 14, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: label }) }),
|
|
1086
|
-
/* @__PURE__ */ jsx(Box, { width: 12, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: currency(summary.cost) }) }),
|
|
1087
|
-
/* @__PURE__ */ jsx(Box, { width: 18, justifyContent: "flex-end", children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1088
|
-
tokens(summary.tokens),
|
|
1089
|
-
" tokens"
|
|
1090
|
-
] }) })
|
|
1091
|
-
] });
|
|
1092
|
-
}
|
|
1093
1798
|
function TableView({ rows: allRows, cursor, expanded, maxRows, cols, onRowClick }) {
|
|
1094
1799
|
const wide = cols > 90;
|
|
1095
1800
|
const base = wide ? { label: 12, input: 10, output: 10, cc: 14, cr: 12, total: 11, cost: 13 } : { label: 8, input: 7, output: 7, cc: 7, cr: 8, total: 0, cost: 11 };
|
|
@@ -1167,7 +1872,7 @@ function TableView({ rows: allRows, cursor, expanded, maxRows, cols, onRowClick
|
|
|
1167
1872
|
allRows.length
|
|
1168
1873
|
] }),
|
|
1169
1874
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1170
|
-
/* @__PURE__ */ jsx(Footer, {})
|
|
1875
|
+
/* @__PURE__ */ jsx(Footer, { hasAccounts: false })
|
|
1171
1876
|
] });
|
|
1172
1877
|
}
|
|
1173
1878
|
function RowDetail({ row, indent }) {
|