tokmon 0.3.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/cli.js +76 -37
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { render } from "ink";
5
5
 
6
6
  // src/app.tsx
7
- import { useState, useEffect } from "react";
7
+ import { useState, useEffect, useRef } from "react";
8
8
  import { Box, Text, useInput, useStdout } from "ink";
9
9
 
10
10
  // src/data.ts
@@ -194,15 +194,14 @@ function isoWeekLabel(ts) {
194
194
  function monthLabel(ts) {
195
195
  return new Date(ts).toISOString().slice(0, 7);
196
196
  }
197
- async function fetchData() {
197
+ async function fetchDashboard() {
198
198
  const now = Date.now();
199
199
  const d = /* @__PURE__ */ new Date();
200
- const lookback = new Date(d.getFullYear(), d.getMonth() - 6, 1).getTime();
201
200
  const monthStart = new Date(d.getFullYear(), d.getMonth(), 1).getTime();
202
201
  const todayStart = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
203
202
  const weekDay = d.getDay();
204
203
  const weekStart = new Date(d.getFullYear(), d.getMonth(), d.getDate() - (weekDay === 0 ? 6 : weekDay - 1)).getTime();
205
- const entries = await loadEntries(lookback);
204
+ const entries = await loadEntries(monthStart);
206
205
  const fiveHoursAgo = now - 5 * 36e5;
207
206
  const blockEntries = entries.filter((e) => e.ts >= fiveHoursAgo);
208
207
  let block = null;
@@ -215,17 +214,21 @@ async function fetchData() {
215
214
  const percent = Math.min(100, (now - oldest) / (5 * 36e5) * 100);
216
215
  block = { spent, projected: burnRate * 5, burnRate, percent, remaining: minutes(remainMs / 6e4) };
217
216
  }
218
- const daily = groupBy(entries, (e) => new Date(e.ts).toISOString().slice(0, 10));
219
- const weekly = groupBy(entries, (e) => isoWeekLabel(e.ts));
220
- const monthly = groupBy(entries, (e) => monthLabel(e.ts));
221
217
  return {
222
218
  today: sum(entries.filter((e) => e.ts >= todayStart)),
223
219
  week: sum(entries.filter((e) => e.ts >= weekStart)),
224
220
  month: sum(entries.filter((e) => e.ts >= monthStart)),
225
- block,
226
- daily,
227
- weekly,
228
- monthly
221
+ block
222
+ };
223
+ }
224
+ async function fetchTable() {
225
+ const d = /* @__PURE__ */ new Date();
226
+ const lookback = new Date(d.getFullYear(), d.getMonth() - 6, 1).getTime();
227
+ const entries = await loadEntries(lookback);
228
+ return {
229
+ daily: groupBy(entries, (e) => new Date(e.ts).toISOString().slice(0, 10)),
230
+ weekly: groupBy(entries, (e) => isoWeekLabel(e.ts)),
231
+ monthly: groupBy(entries, (e) => monthLabel(e.ts))
229
232
  };
230
233
  }
231
234
 
@@ -267,7 +270,9 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
267
270
  var TABS = ["Dashboard", "Table"];
268
271
  var VIEWS = ["Daily", "Weekly", "Monthly"];
269
272
  function App({ interval: cliInterval }) {
270
- const [data, setData] = useState(null);
273
+ const [dashboard, setDashboard] = useState(null);
274
+ const [table, setTable] = useState(null);
275
+ const [tableLoading, setTableLoading] = useState(false);
271
276
  const [error, setError] = useState(null);
272
277
  const [updated, setUpdated] = useState(/* @__PURE__ */ new Date());
273
278
  const [tab, setTab] = useState(0);
@@ -276,6 +281,7 @@ function App({ interval: cliInterval }) {
276
281
  const [showSettings, setShowSettings] = useState(false);
277
282
  const [config, setConfig] = useState(null);
278
283
  const [settingsCursor, setSettingsCursor] = useState(0);
284
+ const tableLoadedOnce = useRef(false);
279
285
  const { stdout } = useStdout();
280
286
  const rows = stdout?.rows ?? 24;
281
287
  const cols = stdout?.columns ?? 80;
@@ -287,6 +293,60 @@ function App({ interval: cliInterval }) {
287
293
  if (c.clearScreen && stdout) stdout.write("\x1B[2J\x1B[H");
288
294
  });
289
295
  }, []);
296
+ useEffect(() => {
297
+ let active = true;
298
+ const load = async () => {
299
+ try {
300
+ const result = await fetchDashboard();
301
+ if (active) {
302
+ setDashboard(result);
303
+ setError(null);
304
+ setUpdated(/* @__PURE__ */ new Date());
305
+ }
306
+ } catch (e) {
307
+ if (active) setError(e instanceof Error ? e.message : String(e));
308
+ }
309
+ };
310
+ load();
311
+ const id = setInterval(load, interval2);
312
+ return () => {
313
+ active = false;
314
+ clearInterval(id);
315
+ };
316
+ }, [interval2]);
317
+ useEffect(() => {
318
+ if (tab !== 1) return;
319
+ if (tableLoadedOnce.current && table) return;
320
+ let active = true;
321
+ setTableLoading(true);
322
+ fetchTable().then((result) => {
323
+ if (active) {
324
+ setTable(result);
325
+ setTableLoading(false);
326
+ tableLoadedOnce.current = true;
327
+ }
328
+ }).catch(() => {
329
+ if (active) setTableLoading(false);
330
+ });
331
+ return () => {
332
+ active = false;
333
+ };
334
+ }, [tab]);
335
+ useEffect(() => {
336
+ if (tab !== 1 || !tableLoadedOnce.current) return;
337
+ let active = true;
338
+ const id = setInterval(async () => {
339
+ try {
340
+ const result = await fetchTable();
341
+ if (active) setTable(result);
342
+ } catch {
343
+ }
344
+ }, Math.max(interval2, 1e4));
345
+ return () => {
346
+ active = false;
347
+ clearInterval(id);
348
+ };
349
+ }, [tab, interval2]);
290
350
  const isTTY = process.stdin.isTTY === true;
291
351
  const settingsItems = 2;
292
352
  const cfg = config ?? { interval: 2, clearScreen: true };
@@ -377,30 +437,9 @@ function App({ interval: cliInterval }) {
377
437
  if (key.pageDown) setScroll((s) => s + Math.max(1, rows - 12));
378
438
  if (key.pageUp) setScroll((s) => Math.max(0, s - Math.max(1, rows - 12)));
379
439
  }, { isActive: isTTY });
380
- useEffect(() => {
381
- let active = true;
382
- const load = async () => {
383
- try {
384
- const result = await fetchData();
385
- if (active) {
386
- setData(result);
387
- setError(null);
388
- setUpdated(/* @__PURE__ */ new Date());
389
- }
390
- } catch (e) {
391
- if (active) setError(e instanceof Error ? e.message : String(e));
392
- }
393
- };
394
- load();
395
- const id = setInterval(load, interval2);
396
- return () => {
397
- active = false;
398
- clearInterval(id);
399
- };
400
- }, [interval2]);
401
440
  if (error) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { color: "red", children: error }) });
402
- if (!data) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Loading..." }) });
403
- const tableData = [data.daily, data.weekly, data.monthly][view];
441
+ if (!dashboard) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Loading..." }) });
442
+ const tableData = table ? [table.daily, table.weekly, table.monthly][view] : [];
404
443
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
405
444
  /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
406
445
  /* @__PURE__ */ jsxs(Box, { children: [
@@ -422,11 +461,11 @@ function App({ interval: cliInterval }) {
422
461
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " Tab s=settings" })
423
462
  ] }),
424
463
  /* @__PURE__ */ jsx(Box, { height: 1 }),
425
- tab === 0 && /* @__PURE__ */ jsx(DashboardView, { data }),
464
+ tab === 0 && /* @__PURE__ */ jsx(DashboardView, { data: dashboard }),
426
465
  tab === 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
427
466
  /* @__PURE__ */ jsx(ViewBar, { views: VIEWS, active: view }),
428
467
  /* @__PURE__ */ jsx(Box, { height: 1 }),
429
- /* @__PURE__ */ jsx(TableView, { rows: tableData, scroll, maxRows: rows - 12, wide: cols > 90 })
468
+ tableLoading && !table ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Loading 6 months of history..." }) : /* @__PURE__ */ jsx(TableView, { rows: tableData, scroll, maxRows: rows - 12, wide: cols > 90 })
430
469
  ] })
431
470
  ] }),
432
471
  /* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokmon",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Terminal dashboard for Claude Code usage and costs",
5
5
  "type": "module",
6
6
  "bin": {