tokmon 0.22.5 → 0.22.7

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # tokmon
2
2
 
3
- Terminal dashboard for **Claude Code**, **Codex**, **Cursor**, **Copilot**, **opencode**, **pi**, **Antigravity**, and **Gemini** — usage, costs, and rate limits, all in one place.
3
+ Terminal dashboard for **Claude Code**, **Codex**, **Cursor**, **Copilot**, **opencode**, **pi**, **Antigravity**, **Gemini**, and **Grok** — usage, costs, and rate limits, all in one place.
4
4
 
5
5
  Built with [Ink](https://github.com/vadimdemedes/ink) and TypeScript.
6
6
 
@@ -34,14 +34,15 @@ Then run `tokmon`. On first launch you'll pick which providers to track; press `
34
34
  |----------|---------------|--------------|
35
35
  | **Claude** | `~/.claude/projects/**/*.jsonl` | Cost & token history, plan (e.g. Max 20x), live 5h / weekly / Sonnet limits |
36
36
  | **Codex** | `~/.codex/sessions/**/rollout-*.jsonl` | Cost & token history, plan, live 5h / weekly limits, credit balance |
37
+ | **Cursor** | Cursor API usage events + local `composerData` | Cost & token history (Today / Week / Month), plan, period spend, on-demand caps |
37
38
  | **opencode** | `~/.local/share/opencode/opencode.db` | Cost & token history across whatever providers opencode routes to (uses its own recorded cost) |
38
39
  | **pi** | `~/.pi/agent/sessions/**/*.jsonl` | Cost & token history (uses pi's own recorded cost) |
40
+ | **Grok** | `~/.grok/logs/unified.jsonl` (+ session model join) | Cost & token history from Grok CLI turns, SuperGrok / credits billing when signed in |
39
41
 
40
42
  **Billing / quota providers** — plan + live quota or spend (no local token history):
41
43
 
42
44
  | Provider | What it reads | What you get |
43
45
  |----------|---------------|--------------|
44
- | **Cursor** | `state.vscdb` + local tracking DBs | Plan, current-period spend, on-demand caps, per-model spend, AI-code activity |
45
46
  | **Copilot** | GitHub token (gh / VS Code) | Plan + premium-request & chat quota |
46
47
  | **Antigravity** | its `state.vscdb` OAuth → Google Cloud Code | Plan + per-pool (Gemini Pro/Flash/Claude) quota |
47
48
  | **Gemini** | `~/.gemini/oauth_creds.json` → Google Cloud Code | Plan + quota (re-run `gemini` to refresh an expired token) |
@@ -70,7 +71,7 @@ A **Peak / Off-Peak** badge appears in the header (Claude only), fetched from [p
70
71
 
71
72
  Per-provider history with a provider selector (`p` / `P`), search (`/`), and sorting (`o`).
72
73
 
73
- For **Claude / Codex** — Daily, Weekly, and Monthly breakdowns (6 months of history). Each row shows models used, input/output/cache tokens, and cost. Press `Enter` to expand a per-model breakdown:
74
+ For **Claude / Codex / Cursor / Grok** — Daily, Weekly, and Monthly breakdowns (6 months of history). Each row shows models used, input/output/cache tokens, and cost. Press `Enter` to expand a per-model breakdown:
74
75
 
75
76
  ```
76
77
  ▸ Apr 7 haiku-4-5, op~ 7.6K 487.0K 10.1M 1.1B $603.89
@@ -78,8 +79,6 @@ For **Claude / Codex** — Daily, Weekly, and Monthly breakdowns (6 months of hi
78
79
  └─ haiku-4-5 100 2.0K 100K 5.0M $2.39
79
80
  ```
80
81
 
81
- For **Cursor** — a per-model spend table (cost, request count, share of total, all-time), sourced from Cursor's local conversation data.
82
-
83
82
  ## Web Dashboard
84
83
 
85
84
  Prefer a browser? `tokmon serve` starts a local web dashboard with the same data as the TUI — charts, global filtering, and shareable images — in a terminal-styled UI. Press `w` (or `W`) inside the TUI to toggle it without leaving the terminal; the header shows `● web :4317` while it's running.
@@ -10,7 +10,6 @@ import {
10
10
  coalesceTables,
11
11
  col,
12
12
  currency,
13
- cursorModelSpend,
14
13
  detectProviders,
15
14
  fetchPeak,
16
15
  modelColor,
@@ -20,7 +19,7 @@ import {
20
19
  time,
21
20
  tokens,
22
21
  withTimeout
23
- } from "./chunk-AQNFQRWV.js";
22
+ } from "./chunk-I4USANOF.js";
24
23
  import {
25
24
  COLOR_PALETTE,
26
25
  DEFAULTS,
@@ -35,7 +34,7 @@ import {
35
34
  sanitizeTyped,
36
35
  saveConfigSync,
37
36
  snapshotCacheFile
38
- } from "./chunk-QTHCHB7S.js";
37
+ } from "./chunk-TNIADJ3W.js";
39
38
  import {
40
39
  glyphs
41
40
  } from "./chunk-RF4GGQGM.js";
@@ -1246,62 +1245,6 @@ function RowDetail({ row, indent }) {
1246
1245
  /* @__PURE__ */ jsx4(Text4, { bold: true, color: "yellow", children: currency(m.cost) })
1247
1246
  ] }, m.name)) });
1248
1247
  }
1249
- var CursorSpendTable = memo4(function CursorSpendTable2({ rows, cursor, maxRows, onRowClick }) {
1250
- if (rows.length === 0) return /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No Cursor spend recorded locally." });
1251
- const total = rows.reduce((a, r) => a + r.usd, 0);
1252
- const totalAmt = rows.reduce((a, r) => a + r.requests, 0);
1253
- const clamped = Math.min(cursor, rows.length - 1);
1254
- const scrollStart = Math.max(0, Math.min(clamped - Math.floor(maxRows / 2), rows.length - maxRows));
1255
- const visible = rows.slice(scrollStart, scrollStart + maxRows);
1256
- const W = { model: 34, cost: 12, amount: 12, share: 8 };
1257
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
1258
- /* @__PURE__ */ jsxs4(Text4, { children: [
1259
- /* @__PURE__ */ jsxs4(Text4, { bold: true, children: [
1260
- " ",
1261
- col("Model", W.model, "left")
1262
- ] }),
1263
- /* @__PURE__ */ jsx4(Text4, { bold: true, children: col("Cost", W.cost) }),
1264
- /* @__PURE__ */ jsx4(Text4, { bold: true, children: col("Amount", W.amount) }),
1265
- /* @__PURE__ */ jsx4(Text4, { bold: true, children: col("Share", W.share) })
1266
- ] }),
1267
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: glyphs().rule.repeat(W.model + W.cost + W.amount + W.share + 2) }),
1268
- visible.map((r, vi) => {
1269
- const idx = scrollStart + vi;
1270
- const selected = idx === clamped;
1271
- const share = total > 0 ? r.usd / total * 100 : 0;
1272
- return /* @__PURE__ */ jsx4(ClickableBox, { onClick: () => onRowClick(idx), children: /* @__PURE__ */ jsxs4(Text4, { inverse: selected, children: [
1273
- /* @__PURE__ */ jsxs4(Text4, { color: selected ? void 0 : "magenta", children: [
1274
- selected ? `${glyphs().caretR} ` : " ",
1275
- col(r.name, W.model, "left")
1276
- ] }),
1277
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: selected ? void 0 : "yellow", children: col(currency(r.usd), W.cost) }),
1278
- /* @__PURE__ */ jsx4(Text4, { children: col(tokens(r.requests), W.amount) }),
1279
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: col(share.toFixed(1) + "%", W.share) })
1280
- ] }) }, r.name);
1281
- }),
1282
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: glyphs().rule.repeat(W.model + W.cost + W.amount + W.share + 2) }),
1283
- /* @__PURE__ */ jsxs4(Text4, { children: [
1284
- /* @__PURE__ */ jsxs4(Text4, { bold: true, color: "greenBright", children: [
1285
- " ",
1286
- col("Total", W.model, "left")
1287
- ] }),
1288
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "yellowBright", children: col(currency(total), W.cost) }),
1289
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "yellow", children: col(tokens(totalAmt), W.amount) }),
1290
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: col("100%", W.share) })
1291
- ] }),
1292
- /* @__PURE__ */ jsx4(Box4, { height: 1 }),
1293
- /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
1294
- "local spend by model (composerData) ",
1295
- glyphs().middot,
1296
- " est. API-equivalent ",
1297
- glyphs().middot,
1298
- " ",
1299
- clamped + 1,
1300
- "/",
1301
- rows.length
1302
- ] })
1303
- ] });
1304
- });
1305
1248
  function fmtLabel(label) {
1306
1249
  if (label.length === 7 && label[4] === "-") {
1307
1250
  return `${MONTHS[Number(label.slice(5, 7))]} '${label.slice(2, 4)}`;
@@ -1965,15 +1908,6 @@ function toStatsMap(snapshot, accounts) {
1965
1908
  }
1966
1909
  return out;
1967
1910
  }
1968
- function toCursorRows(snapshot, accountId) {
1969
- if (!snapshot || !accountId) return null;
1970
- const wa = snapshot.accounts.find((a) => a.id === accountId);
1971
- if (!wa) return null;
1972
- if (wa.billingState === "pending") return null;
1973
- const spend = wa.billing?.modelSpend;
1974
- if (!spend) return [];
1975
- return spend.map((m) => ({ name: m.name, usd: m.usd, requests: m.requests }));
1976
- }
1977
1911
  function pickTable(snapshot, accountIds) {
1978
1912
  if (!snapshot) return null;
1979
1913
  const byId = indexById(snapshot);
@@ -1994,11 +1928,6 @@ var SORTS = [
1994
1928
  { label: "cost", dir: "up" },
1995
1929
  { label: "cost", dir: "down" }
1996
1930
  ];
1997
- var CURSOR_SORTS = [
1998
- { label: "cost", dir: "down" },
1999
- { label: "amount", dir: "down" },
2000
- { label: "model", dir: null }
2001
- ];
2002
1931
  var acctKey = (a) => `${a.id}:${a.homeDir ?? ""}`;
2003
1932
  var clampCaret = (caret, len) => Math.max(0, Math.min(caret, len));
2004
1933
  function spliceInsert(value, caret, text) {
@@ -2098,22 +2027,6 @@ function filterTokenRows(rows, q) {
2098
2027
  const s = q.toLowerCase();
2099
2028
  return rows.filter((r) => r.label.toLowerCase().includes(s) || r.models.some((m) => m.toLowerCase().includes(s)));
2100
2029
  }
2101
- function filterCursorRows(rows, q) {
2102
- if (!q) return rows;
2103
- const s = q.toLowerCase();
2104
- return rows.filter((r) => r.name.toLowerCase().includes(s));
2105
- }
2106
- function sortCursorRows(rows, sortIdx) {
2107
- const out = [...rows];
2108
- switch (sortIdx % CURSOR_SORTS.length) {
2109
- case 1:
2110
- return out.sort((a, b) => b.requests - a.requests);
2111
- case 2:
2112
- return out.sort((a, b) => a.name.localeCompare(b.name));
2113
- default:
2114
- return out.sort((a, b) => b.usd - a.usd);
2115
- }
2116
- }
2117
2030
 
2118
2031
  // src/ui/keybindings.ts
2119
2032
  function handleKey(input, key, ctx) {
@@ -2176,7 +2089,6 @@ function handleKey(input, key, ctx) {
2176
2089
  setExpanded,
2177
2090
  setSort,
2178
2091
  SORTS_FOR,
2179
- tableIsCursor,
2180
2092
  cycleTableModel: cycleTableModel2,
2181
2093
  setView,
2182
2094
  cursor,
@@ -2610,48 +2522,38 @@ function handleKey(input, key, ctx) {
2610
2522
  resetView();
2611
2523
  return;
2612
2524
  }
2613
- if (!tableIsCursor) {
2614
- if (input === "d") {
2615
- setView(0);
2616
- resetView();
2617
- return;
2618
- }
2619
- if (input === "w") {
2620
- setView(1);
2621
- resetView();
2622
- return;
2623
- }
2624
- if (input === "m") {
2625
- cycleTableModel2(1);
2626
- return;
2627
- }
2628
- if (input === "M") {
2629
- cycleTableModel2(-1);
2630
- return;
2631
- }
2632
- if (key.leftArrow) {
2633
- setView((v) => (v - 1 + VIEWS.length) % VIEWS.length);
2634
- resetView();
2635
- return;
2636
- }
2637
- if (key.rightArrow) {
2638
- setView((v) => (v + 1) % VIEWS.length);
2639
- resetView();
2640
- return;
2641
- }
2642
- if (key.return) {
2643
- setExpanded((e) => e === cursor ? -1 : cursor);
2644
- return;
2645
- }
2525
+ if (input === "d") {
2526
+ setView(0);
2527
+ resetView();
2528
+ return;
2646
2529
  }
2647
- } else {
2648
- if (key.leftArrow || key.rightArrow) {
2649
- setTab((t) => (t + 1) % TABS.length);
2530
+ if (input === "w") {
2531
+ setView(1);
2650
2532
  resetView();
2651
2533
  return;
2652
2534
  }
2653
- }
2654
- if (tab === 1 && !tableIsCursor) {
2535
+ if (input === "m") {
2536
+ cycleTableModel2(1);
2537
+ return;
2538
+ }
2539
+ if (input === "M") {
2540
+ cycleTableModel2(-1);
2541
+ return;
2542
+ }
2543
+ if (key.leftArrow) {
2544
+ setView((v) => (v - 1 + VIEWS.length) % VIEWS.length);
2545
+ resetView();
2546
+ return;
2547
+ }
2548
+ if (key.rightArrow) {
2549
+ setView((v) => (v + 1) % VIEWS.length);
2550
+ resetView();
2551
+ return;
2552
+ }
2553
+ if (key.return) {
2554
+ setExpanded((e) => e === cursor ? -1 : cursor);
2555
+ return;
2556
+ }
2655
2557
  if (key.upArrow) {
2656
2558
  setCursor((c) => Math.max(0, c - 1));
2657
2559
  return;
@@ -2668,6 +2570,12 @@ function handleKey(input, key, ctx) {
2668
2570
  setCursor((c) => input === "g" ? 0 : Math.max(0, c - Math.max(1, rows - 12)));
2669
2571
  return;
2670
2572
  }
2573
+ } else {
2574
+ if (key.leftArrow || key.rightArrow) {
2575
+ setTab((t) => (t + 1) % TABS.length);
2576
+ resetView();
2577
+ return;
2578
+ }
2671
2579
  }
2672
2580
  }
2673
2581
 
@@ -2900,7 +2808,7 @@ function useDegradedPolling({ degraded, configReady, showPicker, accountsKey, ac
2900
2808
  let timer;
2901
2809
  const load = async () => {
2902
2810
  try {
2903
- const peakP = accountsRef.current.some((a) => a.providerId === "claude") ? fetchPeak() : Promise.resolve(null);
2811
+ const peakP = accountsRef.current.some((a) => a.providerId === "claude") ? withTimeout(fetchPeak()).catch(() => null) : Promise.resolve(null);
2904
2812
  await Promise.all(accountsRef.current.map(async (acc) => {
2905
2813
  const provider = PROVIDERS[acc.providerId];
2906
2814
  if (!provider.hasBilling || !provider.fetchBilling) return;
@@ -2910,7 +2818,7 @@ function useDegradedPolling({ degraded, configReady, showPicker, accountsKey, ac
2910
2818
  } catch {
2911
2819
  }
2912
2820
  }));
2913
- const p = await withTimeout(peakP);
2821
+ const p = await peakP;
2914
2822
  if (active && p) setPeak(p);
2915
2823
  } finally {
2916
2824
  if (active) timer = setTimeout(load, billingMs);
@@ -3055,7 +2963,6 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3055
2963
  const [tableModel, setTableModel] = useState8(null);
3056
2964
  const [search, setSearch] = useState8("");
3057
2965
  const [searchMode, setSearchMode] = useState8(false);
3058
- const [cursorRowsLocal, setCursorRows] = useState8(null);
3059
2966
  const [showSettings, setShowSettings] = useState8(false);
3060
2967
  const [settingsTab, setSettingsTab] = useState8("general");
3061
2968
  const [settingsCursor, setSettingsCursor] = useState8(0);
@@ -3153,7 +3060,10 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3153
3060
  () => focusId === null ? allGroups : accountsByProvider(visibleAccounts),
3154
3061
  [allGroups, visibleAccounts, focusId]
3155
3062
  );
3156
- const tableProvs = useMemo2(() => allGroups.map((g) => g.provider), [allGroups]);
3063
+ const tableProvs = useMemo2(
3064
+ () => allGroups.map((g) => g.provider).filter((pid) => PROVIDERS[pid].hasUsage),
3065
+ [allGroups]
3066
+ );
3157
3067
  const TOO_SMALL = cols < 40 || rows < 12;
3158
3068
  const allReady = accounts.length > 0 && accounts.every((a) => accountReady(readyInputFor(a.id), a.providerId));
3159
3069
  const { gridBudget } = useMemo2(() => computeChrome(slots, cols, rows), [slots, cols, rows]);
@@ -3193,21 +3103,16 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3193
3103
  };
3194
3104
  const { handlePasteData, isPasteInput } = usePaste(insertText);
3195
3105
  const effTableProvider = tableProvider && tableProvs.includes(tableProvider) ? tableProvider : tableProvs[0] ?? null;
3196
- const tableIsCursor = !!effTableProvider && !PROVIDERS[effTableProvider].hasUsage;
3197
3106
  const tableAccounts = useMemo2(
3198
3107
  () => effTableProvider ? accounts.filter((a) => a.providerId === effTableProvider) : [],
3199
3108
  [accounts, effTableProvider]
3200
3109
  );
3201
- const SORTS_FOR = tableIsCursor ? CURSOR_SORTS : SORTS;
3110
+ const SORTS_FOR = SORTS;
3202
3111
  const tableAccountIds = useMemo2(() => tableAccounts.map((a) => a.id), [tableAccounts]);
3203
3112
  const table = useMemo2(
3204
3113
  () => connected ? pickTable(snapshot, tableAccountIds) : tableLocal,
3205
3114
  [connected, snapshot, tableAccountIds, tableLocal]
3206
3115
  );
3207
- const cursorRows = useMemo2(
3208
- () => connected ? toCursorRows(snapshot, tableAccounts[0]?.id) : cursorRowsLocal,
3209
- [connected, snapshot, tableAccounts, cursorRowsLocal]
3210
- );
3211
3116
  const { showLoader } = useLoader({
3212
3117
  configReady,
3213
3118
  showPicker,
@@ -3236,11 +3141,10 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3236
3141
  );
3237
3142
  useEffect7(() => {
3238
3143
  setTable(null);
3239
- setCursorRows(null);
3240
3144
  setCursor(0);
3241
3145
  setExpanded(-1);
3242
3146
  setTableModel(null);
3243
- setSort(tableIsCursor ? 0 : 1);
3147
+ setSort(1);
3244
3148
  setTableLoading(false);
3245
3149
  }, [tableKey]);
3246
3150
  useEffect7(() => {
@@ -3249,13 +3153,8 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3249
3153
  let timer;
3250
3154
  const fetchOnce = async () => {
3251
3155
  try {
3252
- if (tableIsCursor) {
3253
- const s = await withTimeout(cursorModelSpend(tableAccounts[0]?.homeDir));
3254
- if (active) setCursorRows(s?.models ?? []);
3255
- } else {
3256
- const r = await withTimeout(fetchScopeTable(tableAccounts, tz));
3257
- if (active) setTable(r);
3258
- }
3156
+ const r = await withTimeout(fetchScopeTable(tableAccounts, tz));
3157
+ if (active) setTable(r);
3259
3158
  } catch {
3260
3159
  }
3261
3160
  };
@@ -3392,8 +3291,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3392
3291
  const cur = effTableProvider ? tableProvs.indexOf(effTableProvider) : 0;
3393
3292
  const nextProv = tableProvs[(cur + dir + tableProvs.length) % tableProvs.length];
3394
3293
  setTableProvider(nextProv);
3395
- const nextIsCursor = !!nextProv && !PROVIDERS[nextProv].hasUsage;
3396
- setSort(nextIsCursor ? 0 : 1);
3294
+ setSort(1);
3397
3295
  setCursor(0);
3398
3296
  setExpanded(-1);
3399
3297
  setTableModel(null);
@@ -3426,7 +3324,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3426
3324
  if (webStartingRef.current) return;
3427
3325
  webStartingRef.current = true;
3428
3326
  try {
3429
- const { startWebServer } = await import("./server-56C7MMEZ.js");
3327
+ const { startWebServer } = await import("./server-GA2FKYDQ.js");
3430
3328
  const ctrl = await startWebServer({ config: cfg, log: false });
3431
3329
  webRef.current = ctrl;
3432
3330
  openUrl(ctrl.url);
@@ -3456,10 +3354,9 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3456
3354
  if (idx === cursor) setExpanded((e) => e === idx ? -1 : idx);
3457
3355
  else setCursor(idx);
3458
3356
  }, [cursor]);
3459
- const onRowClickCursor = useCallback4((idx) => setCursor(idx), []);
3460
3357
  const rawTokenRows = useMemo2(
3461
- () => tab === 1 && !tableIsCursor ? table ? [table.daily, table.weekly, table.monthly][view] : [] : [],
3462
- [tab, tableIsCursor, table, view]
3358
+ () => tab === 1 ? table ? [table.daily, table.weekly, table.monthly][view] : [] : [],
3359
+ [tab, table, view]
3463
3360
  );
3464
3361
  const tokenModelOptions = useMemo2(() => tableModelOptions(rawTokenRows), [rawTokenRows]);
3465
3362
  useEffect7(() => {
@@ -3471,12 +3368,8 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3471
3368
  }, [tokenModelOptions, resetView]);
3472
3369
  const activeTableModel = tableModel && tokenModelOptions.includes(tableModel) ? tableModel : null;
3473
3370
  const tokenRows = useMemo2(
3474
- () => tab === 1 && !tableIsCursor ? sortRows(filterTokenRows(filterRowsByModel(rawTokenRows, activeTableModel), search), sort) : [],
3475
- [tab, tableIsCursor, rawTokenRows, activeTableModel, search, sort]
3476
- );
3477
- const cursorTableRows = useMemo2(
3478
- () => tab === 1 && tableIsCursor ? sortCursorRows(filterCursorRows(cursorRows ?? [], search), sort) : [],
3479
- [tab, tableIsCursor, cursorRows, search, sort]
3371
+ () => tab === 1 ? sortRows(filterTokenRows(filterRowsByModel(rawTokenRows, activeTableModel), search), sort) : [],
3372
+ [tab, rawTokenRows, activeTableModel, search, sort]
3480
3373
  );
3481
3374
  useInput((input, key) => handleKey(input, key, {
3482
3375
  showPicker,
@@ -3537,7 +3430,6 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3537
3430
  setExpanded,
3538
3431
  setSort,
3539
3432
  SORTS_FOR,
3540
- tableIsCursor,
3541
3433
  cycleTableModel: cycleTableModelFilter,
3542
3434
  setView,
3543
3435
  cursor,
@@ -3568,7 +3460,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3568
3460
  if (TOO_SMALL && !showSettings) {
3569
3461
  return /* @__PURE__ */ jsx11(TinyFallback, { groups, stats, rows, cols });
3570
3462
  }
3571
- rowCountRef.current = tableIsCursor ? cursorTableRows.length : tokenRows.length;
3463
+ rowCountRef.current = tokenRows.length;
3572
3464
  return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 2, paddingY: 1, height: rows, overflow: "hidden", children: [
3573
3465
  /* @__PURE__ */ jsxs11(Box11, { justifyContent: "space-between", children: [
3574
3466
  /* @__PURE__ */ jsxs11(Box11, { children: [
@@ -3660,8 +3552,8 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3660
3552
  search,
3661
3553
  searchCaret,
3662
3554
  searching: searchMode,
3663
- showPeriod: !tableIsCursor,
3664
- showModel: !tableIsCursor
3555
+ showPeriod: true,
3556
+ showModel: true
3665
3557
  }
3666
3558
  ),
3667
3559
  /* @__PURE__ */ jsx11(Box11, { height: 1 }),
@@ -3669,15 +3561,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3669
3561
  "No providers enabled ",
3670
3562
  glyphs().emDash,
3671
3563
  " press s to pick providers."
3672
- ] }) : tableLoading && !table && !cursorRows ? /* @__PURE__ */ jsx11(Spinner, { label: "Loading history" }) : tableIsCursor ? /* @__PURE__ */ jsx11(
3673
- CursorSpendTable,
3674
- {
3675
- rows: cursorTableRows,
3676
- cursor,
3677
- maxRows: Math.max(1, rows - 16),
3678
- onRowClick: onRowClickCursor
3679
- }
3680
- ) : /* @__PURE__ */ jsx11(
3564
+ ] }) : tableLoading && !table ? /* @__PURE__ */ jsx11(Spinner, { label: "Loading history" }) : /* @__PURE__ */ jsx11(
3681
3565
  TokenTable,
3682
3566
  {
3683
3567
  rows: tokenRows,
@@ -11,7 +11,7 @@ import {
11
11
  namedHex,
12
12
  resolveTimezone,
13
13
  withTimeout
14
- } from "./chunk-AQNFQRWV.js";
14
+ } from "./chunk-I4USANOF.js";
15
15
  import {
16
16
  cacheDir,
17
17
  expandHome,
@@ -19,7 +19,7 @@ import {
19
19
  normalizeConfig,
20
20
  saveConfig,
21
21
  snapshotCacheFile
22
- } from "./chunk-QTHCHB7S.js";
22
+ } from "./chunk-TNIADJ3W.js";
23
23
 
24
24
  // src/web/server.ts
25
25
  import { createServer } from "http";