tokmon 0.12.1 → 0.12.3

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.
Files changed (2) hide show
  1. package/dist/cli.js +222 -242
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -434,16 +434,15 @@ async function readMacKeychain() {
434
434
  }
435
435
  }
436
436
  async function getAccessToken(homeDir) {
437
- if (homeDir) {
438
- const fromFile = await readCredentialsFile(homeDir);
439
- if (fromFile) return fromFile;
440
- return null;
441
- }
442
- if (process.platform === "darwin") {
443
- const token = await readMacKeychain();
444
- if (token) return token;
437
+ const isDefault = !homeDir || homeDir === homedir3();
438
+ if (isDefault) {
439
+ if (process.platform === "darwin") {
440
+ const token = await readMacKeychain();
441
+ if (token) return token;
442
+ }
443
+ return readCredentialsFile(homeDir);
445
444
  }
446
- return readCredentialsFile();
445
+ return readCredentialsFile(homeDir);
447
446
  }
448
447
  var EMPTY = { session: null, weekly: null, sonnet: null, extraUsage: null, peak: null, error: null };
449
448
  async function fetchBilling(homeDir) {
@@ -580,6 +579,9 @@ var DEFAULT_CONFIG = {
580
579
  };
581
580
  var GENERAL_ROWS = 4;
582
581
  var IS_TTY = process.stdin.isTTY === true;
582
+ function truncateName(s, n) {
583
+ return s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
584
+ }
583
585
  function buildSlots(config2) {
584
586
  const slots = [];
585
587
  if (config2.accounts.length === 0) {
@@ -638,27 +640,30 @@ function App({ interval: cliInterval }) {
638
640
  setConfig(c);
639
641
  });
640
642
  }, []);
643
+ const billingMs = cfg.billingInterval * 6e4;
644
+ const slotIdsKey = (activeSlot.id === null && cfg.accounts.length > 0 ? slots.slice(1) : [activeSlot]).map((s) => s.id ?? "__default__").join(",");
641
645
  useEffect(() => {
642
646
  if (!config2) return;
643
647
  let active = true;
644
648
  const slotsToLoad = activeSlot.id === null && cfg.accounts.length > 0 ? slots.slice(1) : [activeSlot];
645
649
  const load = async () => {
646
- try {
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
- }));
655
- if (active) {
656
- setStats(next);
657
- setError(null);
658
- setUpdated(/* @__PURE__ */ new Date());
650
+ await Promise.all(slotsToLoad.map(async (slot) => {
651
+ try {
652
+ const dashboard = await fetchDashboard(tz, slot.homeDir);
653
+ if (!active) return;
654
+ setStats((prev) => {
655
+ const next = new Map(prev);
656
+ const cur = next.get(slotKey(slot)) ?? { slot, dashboard: null, billing: null };
657
+ next.set(slotKey(slot), { ...cur, slot, dashboard });
658
+ return next;
659
+ });
660
+ } catch (e) {
661
+ if (active) setError(e instanceof Error ? e.message : String(e));
659
662
  }
660
- } catch (e) {
661
- if (active) setError(e instanceof Error ? e.message : String(e));
663
+ }));
664
+ if (active) {
665
+ setError(null);
666
+ setUpdated(/* @__PURE__ */ new Date());
662
667
  }
663
668
  };
664
669
  load();
@@ -667,7 +672,33 @@ function App({ interval: cliInterval }) {
667
672
  active = false;
668
673
  clearInterval(id);
669
674
  };
670
- }, [interval2, tz, config2, activeSlot.id]);
675
+ }, [interval2, tz, config2, slotIdsKey]);
676
+ useEffect(() => {
677
+ if (!config2) return;
678
+ let active = true;
679
+ const slotsToLoad = activeSlot.id === null && cfg.accounts.length > 0 ? slots.slice(1) : [activeSlot];
680
+ const load = async () => {
681
+ await Promise.all(slotsToLoad.map(async (slot) => {
682
+ try {
683
+ const billing = await fetchBilling(slot.homeDir);
684
+ if (!active) return;
685
+ setStats((prev) => {
686
+ const next = new Map(prev);
687
+ const cur = next.get(slotKey(slot)) ?? { slot, dashboard: null, billing: null };
688
+ next.set(slotKey(slot), { ...cur, slot, billing });
689
+ return next;
690
+ });
691
+ } catch {
692
+ }
693
+ }));
694
+ };
695
+ load();
696
+ const id = setInterval(load, billingMs);
697
+ return () => {
698
+ active = false;
699
+ clearInterval(id);
700
+ };
701
+ }, [billingMs, config2, slotIdsKey]);
671
702
  useEffect(() => {
672
703
  tableLoadedOnce.current = false;
673
704
  setTable(null);
@@ -813,6 +844,21 @@ function App({ interval: cliInterval }) {
813
844
  activeAccountId: c.activeAccountId === id ? null : c.activeAccountId
814
845
  }));
815
846
  }
847
+ function moveAccount(idx, dir) {
848
+ updateConfig((c) => {
849
+ const next = [...c.accounts];
850
+ const target = idx + dir;
851
+ if (target < 0 || target >= next.length) return c;
852
+ [next[idx], next[target]] = [next[target], next[idx]];
853
+ return { ...c, accounts: next };
854
+ });
855
+ setSettingsCursor((c) => {
856
+ const target = c + dir;
857
+ const min = accountRowsStart;
858
+ const max = accountRowsStart + cfg.accounts.length - 1;
859
+ return Math.max(min, Math.min(max, target));
860
+ });
861
+ }
816
862
  const accountRowsStart = GENERAL_ROWS;
817
863
  const totalSettingsRows = GENERAL_ROWS + cfg.accounts.length + 1;
818
864
  useInput((input, key) => {
@@ -915,6 +961,12 @@ function App({ interval: cliInterval }) {
915
961
  setShowSettings(false);
916
962
  return;
917
963
  }
964
+ const accIdxNav = settingsCursor - accountRowsStart;
965
+ const onAccountRow = accIdxNav >= 0 && accIdxNav < cfg.accounts.length;
966
+ if (onAccountRow && key.shift && (key.upArrow || key.downArrow)) {
967
+ moveAccount(accIdxNav, key.upArrow ? -1 : 1);
968
+ return;
969
+ }
918
970
  if (key.upArrow) {
919
971
  setSettingsCursor((c) => Math.max(0, c - 1));
920
972
  return;
@@ -1346,7 +1398,7 @@ function SettingsView({
1346
1398
  /* @__PURE__ */ jsx(Text, { children: "Add account" })
1347
1399
  ] }),
1348
1400
  /* @__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" })
1401
+ 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 \u21E7\u2191\u2193 reorder \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" })
1350
1402
  ] });
1351
1403
  }
1352
1404
  function AccountFormView({ form, accounts }) {
@@ -1525,70 +1577,46 @@ function ColorField({ value, focused }) {
1525
1577
  /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: " shows on dashboard, account strip, borders" }) })
1526
1578
  ] });
1527
1579
  }
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
- ];
1580
+ function DashboardView({ slots, stats, compact: _compact }) {
1581
+ const slotKey = (s) => s.id ?? "__default__";
1582
+ const items = slots.map((slot) => ({ slot, s: stats.get(slotKey(slot)) })).filter((x) => !!x.s?.dashboard);
1583
+ if (items.length === 0) return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Loading..." });
1584
+ const agg = aggregateUsage(items.map((i) => i.s.dashboard));
1585
+ const isMulti = items.length > 1;
1561
1586
  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
1587
  /* @__PURE__ */ jsxs(
1573
1588
  Box,
1574
1589
  {
1575
1590
  flexDirection: "column",
1576
- marginTop: 1,
1577
1591
  paddingLeft: 1,
1578
1592
  borderStyle: "bold",
1579
- borderColor: slot.color,
1593
+ borderColor: isMulti ? "green" : items[0].slot.color,
1580
1594
  borderRight: false,
1581
1595
  borderTop: false,
1582
1596
  borderBottom: false,
1583
1597
  children: [
1584
- /* @__PURE__ */ jsx(Text, { bold: true, children: "Usage" }),
1598
+ /* @__PURE__ */ jsxs(Box, { children: [
1599
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Claude" }),
1600
+ isMulti && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1601
+ " all accounts (",
1602
+ items.length,
1603
+ ")"
1604
+ ] }),
1605
+ !isMulti && items[0].slot.id && /* @__PURE__ */ jsxs(Fragment, { children: [
1606
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
1607
+ /* @__PURE__ */ jsx(Text, { color: items[0].slot.color, children: "\u25CF " }),
1608
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: items[0].slot.name })
1609
+ ] })
1610
+ ] }),
1585
1611
  /* @__PURE__ */ jsx(Box, { height: 1 }),
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: [
1612
+ /* @__PURE__ */ jsx(SummaryRow, { label: "Today", summary: agg.today }),
1613
+ /* @__PURE__ */ jsx(SummaryRow, { label: "This Week", summary: agg.week }),
1614
+ /* @__PURE__ */ jsx(SummaryRow, { label: "This Month", summary: agg.month }),
1615
+ agg.burnRate > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1588
1616
  /* @__PURE__ */ jsx(Box, { height: 1 }),
1589
1617
  /* @__PURE__ */ jsxs(Box, { children: [
1590
1618
  /* @__PURE__ */ jsx(Box, { width: 14, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Burn rate" }) }),
1591
- /* @__PURE__ */ jsx(Box, { width: 12, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text, { color: "red", children: currency(dashboard.burnRate) }) }),
1619
+ /* @__PURE__ */ jsx(Box, { width: 12, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text, { color: "red", children: currency(agg.burnRate) }) }),
1592
1620
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "/hr" })
1593
1621
  ] })
1594
1622
  ] })
@@ -1596,166 +1624,137 @@ function SoloAccountCard({ slot, dashboard, billing }) {
1596
1624
  }
1597
1625
  ),
1598
1626
  /* @__PURE__ */ jsx(Box, { height: 1 }),
1599
- /* @__PURE__ */ jsxs(
1600
- Box,
1601
- {
1602
- flexDirection: "column",
1603
- paddingLeft: 1,
1604
- borderStyle: "bold",
1605
- borderColor: billing?.error ? "red" : "yellow",
1606
- borderRight: false,
1607
- borderTop: false,
1608
- borderBottom: false,
1609
- children: [
1610
- /* @__PURE__ */ jsx(Text, { bold: true, children: "Rate Limits" }),
1611
- /* @__PURE__ */ jsx(Box, { height: 1 }),
1612
- billing?.error ? /* @__PURE__ */ jsx(Text, { color: "red", children: billing.error }) : billing?.session || billing?.weekly ? /* @__PURE__ */ jsxs(Fragment, { children: [
1613
- billing.session && /* @__PURE__ */ jsx(LimitBar, { label: "Session", pct: billing.session.utilization, resets: billing.session.resetsAt }),
1614
- billing.weekly && /* @__PURE__ */ jsx(LimitBar, { label: "Weekly", pct: billing.weekly.utilization, resets: billing.weekly.resetsAt }),
1615
- billing.sonnet && /* @__PURE__ */ jsx(LimitBar, { label: "Sonnet", pct: billing.sonnet.utilization, resets: billing.sonnet.resetsAt }),
1616
- billing.extraUsage && /* @__PURE__ */ jsxs(Box, { children: [
1617
- /* @__PURE__ */ jsx(Box, { width: 10, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Extra" }) }),
1618
- /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1619
- "$",
1620
- billing.extraUsage.used.toFixed(2)
1621
- ] }),
1622
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1623
- " / $",
1624
- billing.extraUsage.limit.toFixed(2),
1625
- " limit"
1626
- ] })
1627
- ] })
1628
- ] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Fetching..." })
1629
- ]
1630
- }
1631
- )
1627
+ /* @__PURE__ */ jsx(RateLimitsCard, { items })
1632
1628
  ] });
1633
1629
  }
1634
- function DualBarRow({
1630
+ function aggregateUsage(list) {
1631
+ const z = {
1632
+ today: { cost: 0, tokens: 0 },
1633
+ week: { cost: 0, tokens: 0 },
1634
+ month: { cost: 0, tokens: 0 },
1635
+ burnRate: 0
1636
+ };
1637
+ for (const d of list) {
1638
+ z.today.cost += d.today.cost;
1639
+ z.today.tokens += d.today.tokens;
1640
+ z.week.cost += d.week.cost;
1641
+ z.week.tokens += d.week.tokens;
1642
+ z.month.cost += d.month.cost;
1643
+ z.month.tokens += d.month.tokens;
1644
+ z.burnRate += d.burnRate;
1645
+ }
1646
+ return z;
1647
+ }
1648
+ function SummaryRow({ label, summary }) {
1649
+ return /* @__PURE__ */ jsxs(Box, { children: [
1650
+ /* @__PURE__ */ jsx(Box, { width: 14, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: label }) }),
1651
+ /* @__PURE__ */ jsx(Box, { width: 12, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: currency(summary.cost) }) }),
1652
+ /* @__PURE__ */ jsx(Box, { width: 18, justifyContent: "flex-end", children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1653
+ tokens(summary.tokens),
1654
+ " tokens"
1655
+ ] }) })
1656
+ ] });
1657
+ }
1658
+ function RateLimitsCard({ items }) {
1659
+ const anyData = items.some((i) => i.s.billing?.session || i.s.billing?.weekly || i.s.billing?.sonnet);
1660
+ const anyError = items.some((i) => i.s.billing?.error);
1661
+ const borderColor = !anyData && anyError ? "red" : "yellow";
1662
+ return /* @__PURE__ */ jsxs(
1663
+ Box,
1664
+ {
1665
+ flexDirection: "column",
1666
+ paddingLeft: 1,
1667
+ borderStyle: "bold",
1668
+ borderColor,
1669
+ borderRight: false,
1670
+ borderTop: false,
1671
+ borderBottom: false,
1672
+ children: [
1673
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Rate Limits" }),
1674
+ /* @__PURE__ */ jsx(Box, { height: 1 }),
1675
+ !anyData ? anyError ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: items.map(({ slot, s }) => s.billing?.error && /* @__PURE__ */ jsxs(Box, { children: [
1676
+ /* @__PURE__ */ jsx(Text, { color: slot.color, children: "\u25CF " }),
1677
+ /* @__PURE__ */ jsx(Box, { width: 22, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncateName(slot.name, 20) }) }),
1678
+ /* @__PURE__ */ jsx(Text, { color: "red", children: s.billing.error })
1679
+ ] }, slot.id ?? "__default__")) }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Fetching..." }) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1680
+ /* @__PURE__ */ jsx(MetricBlock, { label: "Total Usage", sublabel: "5h session", pick: (b) => b?.session, items, showResets: true }),
1681
+ /* @__PURE__ */ jsx(MetricBlock, { label: "This Week", sublabel: "7-day", pick: (b) => b?.weekly, items, showResets: true }),
1682
+ /* @__PURE__ */ jsx(MetricBlock, { label: "Sonnet", sublabel: "7-day sonnet", pick: (b) => b?.sonnet, items }),
1683
+ items.some((i) => i.s.billing?.extraUsage) && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
1684
+ /* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: "Extra Usage" }),
1685
+ items.map(({ slot, s }) => {
1686
+ const e = s.billing?.extraUsage;
1687
+ if (!e) return null;
1688
+ return /* @__PURE__ */ jsxs(Box, { children: [
1689
+ /* @__PURE__ */ jsx(Text, { color: slot.color, children: "\u25CF " }),
1690
+ /* @__PURE__ */ jsx(Box, { width: 22, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncateName(slot.name, 20) }) }),
1691
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1692
+ "$",
1693
+ e.used.toFixed(2)
1694
+ ] }),
1695
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1696
+ " / $",
1697
+ e.limit.toFixed(2)
1698
+ ] })
1699
+ ] }, slot.id ?? "__default__");
1700
+ })
1701
+ ] })
1702
+ ] })
1703
+ ]
1704
+ }
1705
+ );
1706
+ }
1707
+ function MetricBlock({
1635
1708
  label,
1636
- cost,
1637
- tokens: tokens2,
1638
- maxCost,
1639
- maxTokens,
1640
- color
1709
+ sublabel,
1710
+ pick,
1711
+ items,
1712
+ showResets
1641
1713
  }) {
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: [
1714
+ const rows = items.map((i) => ({ slot: i.slot, lim: pick(i.s.billing) ?? null })).filter((r) => r.lim !== null);
1715
+ if (rows.length === 0) return null;
1716
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
1646
1717
  /* @__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) }) })
1718
+ /* @__PURE__ */ jsx(Text, { bold: true, children: label }),
1719
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1720
+ " ",
1721
+ sublabel
1722
+ ] })
1652
1723
  ] }),
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 }) => ({
1724
+ rows.map(({ slot, lim }) => /* @__PURE__ */ jsx(
1725
+ AccountLimitBar,
1726
+ {
1677
1727
  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
- ] })
1728
+ pct: lim.utilization,
1729
+ resets: showResets ? lim.resetsAt : null,
1730
+ showName: items.length > 1
1731
+ },
1732
+ slot.id ?? "__default__"
1733
+ ))
1702
1734
  ] });
1703
1735
  }
1704
- function ComparisonRow({
1736
+ function AccountLimitBar({
1705
1737
  slot,
1706
- cost,
1707
- tokens: tokens2,
1708
- maxCost,
1709
- maxTokens
1738
+ pct,
1739
+ resets,
1740
+ showName
1710
1741
  }) {
1711
- const W = 22;
1712
- const c = bar(cost, maxCost, W);
1713
- const t = bar(tokens2, maxTokens, W);
1742
+ const width = 28;
1743
+ const filled = Math.max(0, Math.min(width, Math.round(pct / 100 * width)));
1714
1744
  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) }) })
1745
+ /* @__PURE__ */ jsx(Text, { color: slot.color, children: "\u25CF " }),
1746
+ showName ? /* @__PURE__ */ jsx(Box, { width: 22, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncateName(slot.name, 20) }) }) : /* @__PURE__ */ jsx(Box, { width: 2, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
1747
+ /* @__PURE__ */ jsx(Text, { color: slot.color, children: "\u2501".repeat(filled) }),
1748
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(width - filled) }),
1749
+ /* @__PURE__ */ jsx(Text, { children: " " }),
1750
+ /* @__PURE__ */ jsx(Box, { width: 5, justifyContent: "flex-end", children: /* @__PURE__ */ jsxs(Text, { bold: true, children: [
1751
+ Math.round(pct),
1752
+ "%"
1753
+ ] }) }),
1754
+ resets && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1755
+ " resets ",
1756
+ resets
1757
+ ] })
1759
1758
  ] });
1760
1759
  }
1761
1760
  function PeakBadge({ peak }) {
@@ -1776,25 +1775,6 @@ function fmtMinutes(mins) {
1776
1775
  const m = mins % 60;
1777
1776
  return m === 0 ? `${h}h` : `${h}h ${m}m`;
1778
1777
  }
1779
- function LimitBar({ label, pct, resets }) {
1780
- const width = 30;
1781
- const filled = Math.round(pct / 100 * width);
1782
- const color = pct >= 80 ? "red" : pct >= 50 ? "yellow" : "green";
1783
- return /* @__PURE__ */ jsxs(Box, { children: [
1784
- /* @__PURE__ */ jsx(Box, { width: 10, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: label }) }),
1785
- /* @__PURE__ */ jsx(Text, { color, children: "\u2501".repeat(filled) }),
1786
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(width - filled) }),
1787
- /* @__PURE__ */ jsx(Text, { children: " " }),
1788
- /* @__PURE__ */ jsxs(Text, { bold: true, children: [
1789
- Math.round(pct),
1790
- "%"
1791
- ] }),
1792
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1793
- " resets ",
1794
- resets
1795
- ] })
1796
- ] });
1797
- }
1798
1778
  function TableView({ rows: allRows, cursor, expanded, maxRows, cols, onRowClick }) {
1799
1779
  const wide = cols > 90;
1800
1780
  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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokmon",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "description": "Terminal dashboard for Claude Code usage and costs",
5
5
  "type": "module",
6
6
  "bin": {