tokmon 0.3.0 → 0.3.2
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 +85 -40
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
|
|
6
6
|
// src/app.tsx
|
|
7
|
-
import { useState, useEffect } from "react";
|
|
8
|
-
import { Box, Text, useInput, useStdout } from "ink";
|
|
7
|
+
import { useState, useEffect, useRef } from "react";
|
|
8
|
+
import { Box, Text, useInput, useStdout, useApp } from "ink";
|
|
9
9
|
|
|
10
10
|
// src/data.ts
|
|
11
11
|
import { readdir, stat as fsStat } from "fs/promises";
|
|
@@ -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
|
|
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(
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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 [
|
|
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,61 @@ 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]);
|
|
350
|
+
const { exit } = useApp();
|
|
290
351
|
const isTTY = process.stdin.isTTY === true;
|
|
291
352
|
const settingsItems = 2;
|
|
292
353
|
const cfg = config ?? { interval: 2, clearScreen: true };
|
|
@@ -320,6 +381,10 @@ function App({ interval: cliInterval }) {
|
|
|
320
381
|
}
|
|
321
382
|
return;
|
|
322
383
|
}
|
|
384
|
+
if (input === "q") {
|
|
385
|
+
exit();
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
323
388
|
if (input === "s") {
|
|
324
389
|
setShowSettings(true);
|
|
325
390
|
return;
|
|
@@ -377,30 +442,9 @@ function App({ interval: cliInterval }) {
|
|
|
377
442
|
if (key.pageDown) setScroll((s) => s + Math.max(1, rows - 12));
|
|
378
443
|
if (key.pageUp) setScroll((s) => Math.max(0, s - Math.max(1, rows - 12)));
|
|
379
444
|
}, { 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
445
|
if (error) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { color: "red", children: error }) });
|
|
402
|
-
if (!
|
|
403
|
-
const tableData = [
|
|
446
|
+
if (!dashboard) return /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Loading..." }) });
|
|
447
|
+
const tableData = table ? [table.daily, table.weekly, table.monthly][view] : [];
|
|
404
448
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
405
449
|
/* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
|
|
406
450
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
@@ -419,14 +463,14 @@ function App({ interval: cliInterval }) {
|
|
|
419
463
|
showSettings ? /* @__PURE__ */ jsx(SettingsView, { config: cfg, cursor: settingsCursor }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
420
464
|
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
421
465
|
/* @__PURE__ */ jsx(TabBar, { tabs: TABS, active: tab }),
|
|
422
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " Tab
|
|
466
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " Tab/\u2190\u2192" })
|
|
423
467
|
] }),
|
|
424
468
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
425
|
-
tab === 0 && /* @__PURE__ */ jsx(DashboardView, { data }),
|
|
469
|
+
tab === 0 && /* @__PURE__ */ jsx(DashboardView, { data: dashboard }),
|
|
426
470
|
tab === 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
427
471
|
/* @__PURE__ */ jsx(ViewBar, { views: VIEWS, active: view }),
|
|
428
472
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
429
|
-
/* @__PURE__ */ jsx(TableView, { rows: tableData, scroll, maxRows: rows - 12, wide: cols > 90 })
|
|
473
|
+
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
474
|
] })
|
|
431
475
|
] }),
|
|
432
476
|
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
@@ -434,7 +478,8 @@ function App({ interval: cliInterval }) {
|
|
|
434
478
|
/* @__PURE__ */ jsx(Text, { children: "David Ilie" }),
|
|
435
479
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " (" }),
|
|
436
480
|
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "davidilie.com" }),
|
|
437
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ")" })
|
|
481
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ") \xB7 " }),
|
|
482
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "s=settings q=quit" })
|
|
438
483
|
] })
|
|
439
484
|
] });
|
|
440
485
|
}
|