tokmon 0.10.0 → 0.11.0
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 +206 -39
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ 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 = { interval: 2, billingInterval: 5, clearScreen: true };
|
|
11
|
+
var DEFAULTS = { interval: 2, billingInterval: 5, clearScreen: true, timezone: null };
|
|
12
12
|
function configDir() {
|
|
13
13
|
if (process.platform === "win32") {
|
|
14
14
|
return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "tokmon");
|
|
@@ -45,6 +45,117 @@ import { createReadStream } from "fs";
|
|
|
45
45
|
import { createInterface } from "readline";
|
|
46
46
|
import { join as join2 } from "path";
|
|
47
47
|
import { homedir as homedir2 } from "os";
|
|
48
|
+
|
|
49
|
+
// src/tz.ts
|
|
50
|
+
function systemTimezone() {
|
|
51
|
+
try {
|
|
52
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
53
|
+
} catch {
|
|
54
|
+
return "UTC";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function isValidTimezone(tz) {
|
|
58
|
+
try {
|
|
59
|
+
new Intl.DateTimeFormat("en-CA", { timeZone: tz });
|
|
60
|
+
return true;
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function resolveTimezone(cfg) {
|
|
66
|
+
if (!cfg) return systemTimezone();
|
|
67
|
+
return isValidTimezone(cfg) ? cfg : systemTimezone();
|
|
68
|
+
}
|
|
69
|
+
var dayFmtCache = /* @__PURE__ */ new Map();
|
|
70
|
+
function dayFmt(tz) {
|
|
71
|
+
let f = dayFmtCache.get(tz);
|
|
72
|
+
if (!f) {
|
|
73
|
+
f = new Intl.DateTimeFormat("en-CA", {
|
|
74
|
+
timeZone: tz,
|
|
75
|
+
year: "numeric",
|
|
76
|
+
month: "2-digit",
|
|
77
|
+
day: "2-digit"
|
|
78
|
+
});
|
|
79
|
+
dayFmtCache.set(tz, f);
|
|
80
|
+
}
|
|
81
|
+
return f;
|
|
82
|
+
}
|
|
83
|
+
function dayKey(ts, tz) {
|
|
84
|
+
return dayFmt(tz).format(new Date(ts));
|
|
85
|
+
}
|
|
86
|
+
function monthKey(ts, tz) {
|
|
87
|
+
return dayKey(ts, tz).slice(0, 7);
|
|
88
|
+
}
|
|
89
|
+
var partsFmtCache = /* @__PURE__ */ new Map();
|
|
90
|
+
function partsFmt(tz) {
|
|
91
|
+
let f = partsFmtCache.get(tz);
|
|
92
|
+
if (!f) {
|
|
93
|
+
f = new Intl.DateTimeFormat("en-US", {
|
|
94
|
+
timeZone: tz,
|
|
95
|
+
hourCycle: "h23",
|
|
96
|
+
year: "numeric",
|
|
97
|
+
month: "2-digit",
|
|
98
|
+
day: "2-digit",
|
|
99
|
+
hour: "2-digit",
|
|
100
|
+
minute: "2-digit",
|
|
101
|
+
second: "2-digit",
|
|
102
|
+
weekday: "short"
|
|
103
|
+
});
|
|
104
|
+
partsFmtCache.set(tz, f);
|
|
105
|
+
}
|
|
106
|
+
return f;
|
|
107
|
+
}
|
|
108
|
+
var WEEKDAY_MAP = {
|
|
109
|
+
Sun: 0,
|
|
110
|
+
Mon: 1,
|
|
111
|
+
Tue: 2,
|
|
112
|
+
Wed: 3,
|
|
113
|
+
Thu: 4,
|
|
114
|
+
Fri: 5,
|
|
115
|
+
Sat: 6
|
|
116
|
+
};
|
|
117
|
+
function tzParts(ts, tz) {
|
|
118
|
+
const parts = partsFmt(tz).formatToParts(new Date(ts));
|
|
119
|
+
const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
|
|
120
|
+
return {
|
|
121
|
+
y: Number(get("year")),
|
|
122
|
+
m: Number(get("month")),
|
|
123
|
+
d: Number(get("day")),
|
|
124
|
+
hh: Number(get("hour")),
|
|
125
|
+
mm: Number(get("minute")),
|
|
126
|
+
ss: Number(get("second")),
|
|
127
|
+
weekday: WEEKDAY_MAP[get("weekday")] ?? 0
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function instantFromTz(y, m, d, hh, mm, ss, tz) {
|
|
131
|
+
const guess = Date.UTC(y, m - 1, d, hh, mm, ss);
|
|
132
|
+
const r = tzParts(guess, tz);
|
|
133
|
+
const rendered = Date.UTC(r.y, r.m - 1, r.d, r.hh, r.mm, r.ss);
|
|
134
|
+
const offset = rendered - guess;
|
|
135
|
+
return guess - offset;
|
|
136
|
+
}
|
|
137
|
+
function startOfDay(ts, tz) {
|
|
138
|
+
const p = tzParts(ts, tz);
|
|
139
|
+
return instantFromTz(p.y, p.m, p.d, 0, 0, 0, tz);
|
|
140
|
+
}
|
|
141
|
+
function startOfMonth(ts, tz) {
|
|
142
|
+
const p = tzParts(ts, tz);
|
|
143
|
+
return instantFromTz(p.y, p.m, 1, 0, 0, 0, tz);
|
|
144
|
+
}
|
|
145
|
+
function startOfWeek(ts, tz) {
|
|
146
|
+
const p = tzParts(ts, tz);
|
|
147
|
+
const offset = p.weekday === 0 ? 6 : p.weekday - 1;
|
|
148
|
+
return instantFromTz(p.y, p.m, p.d - offset, 0, 0, 0, tz);
|
|
149
|
+
}
|
|
150
|
+
function monthsAgoStart(ts, months, tz) {
|
|
151
|
+
const p = tzParts(ts, tz);
|
|
152
|
+
return instantFromTz(p.y, p.m - months, 1, 0, 0, 0, tz);
|
|
153
|
+
}
|
|
154
|
+
function weekKey(ts, tz) {
|
|
155
|
+
return dayKey(startOfWeek(ts, tz), tz);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/data.ts
|
|
48
159
|
var PRICING = {
|
|
49
160
|
"claude-opus-4": { i: 5e-6, o: 25e-6, cc: 625e-8, cr: 5e-7 },
|
|
50
161
|
"claude-sonnet-4": { i: 3e-6, o: 15e-6, cc: 375e-8, cr: 3e-7 },
|
|
@@ -207,24 +318,11 @@ function groupBy(entries, keyFn) {
|
|
|
207
318
|
}
|
|
208
319
|
return rows.sort((a, b) => a.label.localeCompare(b.label));
|
|
209
320
|
}
|
|
210
|
-
function
|
|
211
|
-
const d = new Date(ts);
|
|
212
|
-
const day = d.getDay();
|
|
213
|
-
const mondayOffset = day === 0 ? 6 : day - 1;
|
|
214
|
-
const monday = new Date(d);
|
|
215
|
-
monday.setDate(d.getDate() - mondayOffset);
|
|
216
|
-
return monday.toISOString().slice(0, 10);
|
|
217
|
-
}
|
|
218
|
-
function monthLabel(ts) {
|
|
219
|
-
return new Date(ts).toISOString().slice(0, 7);
|
|
220
|
-
}
|
|
221
|
-
async function fetchDashboard() {
|
|
222
|
-
const d = /* @__PURE__ */ new Date();
|
|
223
|
-
const monthStart = new Date(d.getFullYear(), d.getMonth(), 1).getTime();
|
|
224
|
-
const todayStart = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
225
|
-
const weekDay = d.getDay();
|
|
226
|
-
const weekStart = new Date(d.getFullYear(), d.getMonth(), d.getDate() - (weekDay === 0 ? 6 : weekDay - 1)).getTime();
|
|
321
|
+
async function fetchDashboard(tz) {
|
|
227
322
|
const now = Date.now();
|
|
323
|
+
const monthStart = startOfMonth(now, tz);
|
|
324
|
+
const todayStart = startOfDay(now, tz);
|
|
325
|
+
const weekStart = startOfWeek(now, tz);
|
|
228
326
|
const entries = await loadEntries(monthStart);
|
|
229
327
|
const todayEntries = entries.filter((e) => e.ts >= todayStart);
|
|
230
328
|
let burnRate = 0;
|
|
@@ -245,14 +343,13 @@ async function fetchDashboard() {
|
|
|
245
343
|
burnRate
|
|
246
344
|
};
|
|
247
345
|
}
|
|
248
|
-
async function fetchTable() {
|
|
249
|
-
const
|
|
250
|
-
const lookback = new Date(d.getFullYear(), d.getMonth() - 6, 1).getTime();
|
|
346
|
+
async function fetchTable(tz) {
|
|
347
|
+
const lookback = monthsAgoStart(Date.now(), 6, tz);
|
|
251
348
|
const entries = await loadEntries(lookback);
|
|
252
349
|
return {
|
|
253
|
-
daily: groupBy(entries, (e) =>
|
|
254
|
-
weekly: groupBy(entries, (e) =>
|
|
255
|
-
monthly: groupBy(entries, (e) =>
|
|
350
|
+
daily: groupBy(entries, (e) => dayKey(e.ts, tz)),
|
|
351
|
+
weekly: groupBy(entries, (e) => weekKey(e.ts, tz)),
|
|
352
|
+
monthly: groupBy(entries, (e) => monthKey(e.ts, tz))
|
|
256
353
|
};
|
|
257
354
|
}
|
|
258
355
|
|
|
@@ -393,11 +490,12 @@ function tokens(value) {
|
|
|
393
490
|
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
|
|
394
491
|
return String(value);
|
|
395
492
|
}
|
|
396
|
-
function time(date) {
|
|
493
|
+
function time(date, tz) {
|
|
397
494
|
return date.toLocaleTimeString(void 0, {
|
|
398
495
|
hour: "2-digit",
|
|
399
496
|
minute: "2-digit",
|
|
400
|
-
second: "2-digit"
|
|
497
|
+
second: "2-digit",
|
|
498
|
+
timeZone: tz
|
|
401
499
|
});
|
|
402
500
|
}
|
|
403
501
|
var SHORT_MONTHS = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
@@ -418,7 +516,8 @@ var VIEWS = ["Daily", "Weekly", "Monthly"];
|
|
|
418
516
|
var SORTS = ["date \u2191", "date \u2193", "cost \u2191", "cost \u2193"];
|
|
419
517
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
420
518
|
var MONTHS = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
421
|
-
var DEFAULT_CONFIG = { interval: 2, billingInterval: 5, clearScreen: true };
|
|
519
|
+
var DEFAULT_CONFIG = { interval: 2, billingInterval: 5, clearScreen: true, timezone: null };
|
|
520
|
+
var SETTINGS_ROWS = 4;
|
|
422
521
|
var IS_TTY = process.stdin.isTTY === true;
|
|
423
522
|
function App({ interval: cliInterval }) {
|
|
424
523
|
const [dashboard, setDashboard] = useState(null);
|
|
@@ -431,10 +530,12 @@ function App({ interval: cliInterval }) {
|
|
|
431
530
|
const [view, setView] = useState(0);
|
|
432
531
|
const [cursor, setCursor] = useState(0);
|
|
433
532
|
const [expanded, setExpanded] = useState(-1);
|
|
434
|
-
const [sort, setSort] = useState(
|
|
533
|
+
const [sort, setSort] = useState(1);
|
|
435
534
|
const [showSettings, setShowSettings] = useState(false);
|
|
436
535
|
const [config2, setConfig] = useState(null);
|
|
437
536
|
const [settingsCursor, setSettingsCursor] = useState(0);
|
|
537
|
+
const [tzEdit, setTzEdit] = useState(null);
|
|
538
|
+
const [tzError, setTzError] = useState(null);
|
|
438
539
|
const tableLoadedOnce = useRef(false);
|
|
439
540
|
const { stdout } = useStdout();
|
|
440
541
|
const { exit } = useApp();
|
|
@@ -442,6 +543,7 @@ function App({ interval: cliInterval }) {
|
|
|
442
543
|
const cols = stdout?.columns ?? 80;
|
|
443
544
|
const interval2 = cliInterval ?? (config2?.interval ?? 2) * 1e3;
|
|
444
545
|
const cfg = config2 ?? DEFAULT_CONFIG;
|
|
546
|
+
const tz = resolveTimezone(cfg.timezone);
|
|
445
547
|
useEffect(() => {
|
|
446
548
|
loadConfig().then((c) => {
|
|
447
549
|
if (cliInterval) c = { ...c, interval: cliInterval / 1e3 };
|
|
@@ -452,7 +554,7 @@ function App({ interval: cliInterval }) {
|
|
|
452
554
|
let active = true;
|
|
453
555
|
const load = async () => {
|
|
454
556
|
try {
|
|
455
|
-
const result = await fetchDashboard();
|
|
557
|
+
const result = await fetchDashboard(tz);
|
|
456
558
|
if (active) {
|
|
457
559
|
setDashboard(result);
|
|
458
560
|
setError(null);
|
|
@@ -468,7 +570,7 @@ function App({ interval: cliInterval }) {
|
|
|
468
570
|
active = false;
|
|
469
571
|
clearInterval(id);
|
|
470
572
|
};
|
|
471
|
-
}, [interval2]);
|
|
573
|
+
}, [interval2, tz]);
|
|
472
574
|
const billingMs = cfg.billingInterval * 6e4;
|
|
473
575
|
useEffect(() => {
|
|
474
576
|
let active = true;
|
|
@@ -483,12 +585,16 @@ function App({ interval: cliInterval }) {
|
|
|
483
585
|
clearInterval(id);
|
|
484
586
|
};
|
|
485
587
|
}, [billingMs]);
|
|
588
|
+
useEffect(() => {
|
|
589
|
+
tableLoadedOnce.current = false;
|
|
590
|
+
setTable(null);
|
|
591
|
+
}, [tz]);
|
|
486
592
|
useEffect(() => {
|
|
487
593
|
if (tab !== 1) return;
|
|
488
594
|
if (tableLoadedOnce.current && table) return;
|
|
489
595
|
let active = true;
|
|
490
596
|
setTableLoading(true);
|
|
491
|
-
fetchTable().then((result) => {
|
|
597
|
+
fetchTable(tz).then((result) => {
|
|
492
598
|
if (active) {
|
|
493
599
|
setTable(result);
|
|
494
600
|
setTableLoading(false);
|
|
@@ -500,13 +606,13 @@ function App({ interval: cliInterval }) {
|
|
|
500
606
|
return () => {
|
|
501
607
|
active = false;
|
|
502
608
|
};
|
|
503
|
-
}, [tab]);
|
|
609
|
+
}, [tab, tz]);
|
|
504
610
|
useEffect(() => {
|
|
505
611
|
if (tab !== 1 || !tableLoadedOnce.current) return;
|
|
506
612
|
let active = true;
|
|
507
613
|
const id = setInterval(async () => {
|
|
508
614
|
try {
|
|
509
|
-
const result = await fetchTable();
|
|
615
|
+
const result = await fetchTable(tz);
|
|
510
616
|
if (active) setTable(result);
|
|
511
617
|
} catch {
|
|
512
618
|
}
|
|
@@ -515,7 +621,7 @@ function App({ interval: cliInterval }) {
|
|
|
515
621
|
active = false;
|
|
516
622
|
clearInterval(id);
|
|
517
623
|
};
|
|
518
|
-
}, [tab, interval2]);
|
|
624
|
+
}, [tab, interval2, tz]);
|
|
519
625
|
const resetView = useCallback(() => {
|
|
520
626
|
setCursor(0);
|
|
521
627
|
setExpanded(-1);
|
|
@@ -535,6 +641,39 @@ function App({ interval: cliInterval }) {
|
|
|
535
641
|
};
|
|
536
642
|
}, [tab]);
|
|
537
643
|
useInput((input, key) => {
|
|
644
|
+
if (showSettings && tzEdit !== null) {
|
|
645
|
+
if (key.escape) {
|
|
646
|
+
setTzEdit(null);
|
|
647
|
+
setTzError(null);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (key.return) {
|
|
651
|
+
const val = tzEdit.trim();
|
|
652
|
+
if (val === "" || val.toLowerCase() === "system") {
|
|
653
|
+
updateConfig((c) => ({ ...c, timezone: null }));
|
|
654
|
+
setTzEdit(null);
|
|
655
|
+
setTzError(null);
|
|
656
|
+
} else if (isValidTimezone(val)) {
|
|
657
|
+
updateConfig((c) => ({ ...c, timezone: val }));
|
|
658
|
+
setTzEdit(null);
|
|
659
|
+
setTzError(null);
|
|
660
|
+
} else {
|
|
661
|
+
setTzError(`Invalid: ${val}`);
|
|
662
|
+
}
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (key.backspace || key.delete) {
|
|
666
|
+
setTzEdit((s) => (s ?? "").slice(0, -1));
|
|
667
|
+
setTzError(null);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
if (input && !key.ctrl && !key.meta) {
|
|
671
|
+
setTzEdit((s) => (s ?? "") + input);
|
|
672
|
+
setTzError(null);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
538
677
|
if (input === "q") {
|
|
539
678
|
exit();
|
|
540
679
|
return;
|
|
@@ -542,7 +681,7 @@ function App({ interval: cliInterval }) {
|
|
|
542
681
|
if (showSettings) {
|
|
543
682
|
if (key.escape || input === "s") setShowSettings(false);
|
|
544
683
|
if (key.upArrow) setSettingsCursor((c) => Math.max(0, c - 1));
|
|
545
|
-
if (key.downArrow) setSettingsCursor((c) => Math.min(
|
|
684
|
+
if (key.downArrow) setSettingsCursor((c) => Math.min(SETTINGS_ROWS - 1, c + 1));
|
|
546
685
|
if (settingsCursor === 0) {
|
|
547
686
|
if (key.leftArrow) updateConfig((c) => ({ ...c, interval: Math.max(1, c.interval - 1) }));
|
|
548
687
|
if (key.rightArrow) updateConfig((c) => ({ ...c, interval: c.interval + 1 }));
|
|
@@ -554,6 +693,15 @@ function App({ interval: cliInterval }) {
|
|
|
554
693
|
if (settingsCursor === 2 && (key.leftArrow || key.rightArrow || key.return)) {
|
|
555
694
|
updateConfig((c) => ({ ...c, clearScreen: !c.clearScreen }));
|
|
556
695
|
}
|
|
696
|
+
if (settingsCursor === 3) {
|
|
697
|
+
if (key.return) {
|
|
698
|
+
setTzEdit(cfg.timezone ?? "");
|
|
699
|
+
setTzError(null);
|
|
700
|
+
}
|
|
701
|
+
if (key.leftArrow || key.rightArrow) {
|
|
702
|
+
updateConfig((c) => ({ ...c, timezone: c.timezone === null ? systemTimezone() : null }));
|
|
703
|
+
}
|
|
704
|
+
}
|
|
557
705
|
return;
|
|
558
706
|
}
|
|
559
707
|
if (input === "s") {
|
|
@@ -667,10 +815,10 @@ function App({ interval: cliInterval }) {
|
|
|
667
815
|
/* @__PURE__ */ jsx(PeakBadge, { peak: billing.peak }),
|
|
668
816
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " })
|
|
669
817
|
] }),
|
|
670
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: time(updated) })
|
|
818
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: time(updated, tz) })
|
|
671
819
|
] })
|
|
672
820
|
] }),
|
|
673
|
-
showSettings ? /* @__PURE__ */ jsx(SettingsView, { config: cfg, cursor: settingsCursor }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
821
|
+
showSettings ? /* @__PURE__ */ jsx(SettingsView, { config: cfg, cursor: settingsCursor, tzEdit, tzError, resolvedTz: tz }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
674
822
|
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
675
823
|
/* @__PURE__ */ jsx(TabBar, { tabs: TABS, active: tab, onSelect: (i) => {
|
|
676
824
|
setTab(i);
|
|
@@ -753,7 +901,9 @@ function sortRows(rows, sortIdx) {
|
|
|
753
901
|
return sorted;
|
|
754
902
|
}
|
|
755
903
|
}
|
|
756
|
-
function SettingsView({ config: config2, cursor }) {
|
|
904
|
+
function SettingsView({ config: config2, cursor, tzEdit, tzError, resolvedTz }) {
|
|
905
|
+
const editing = tzEdit !== null;
|
|
906
|
+
const tzDisplay = config2.timezone === null ? `System (${resolvedTz})` : config2.timezone;
|
|
757
907
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
758
908
|
/* @__PURE__ */ jsx(Text, { bold: true, children: "Settings" }),
|
|
759
909
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: configLocation() }),
|
|
@@ -804,8 +954,25 @@ function SettingsView({ config: config2, cursor }) {
|
|
|
804
954
|
/* @__PURE__ */ jsx(Text, { children: "Clear screen " }),
|
|
805
955
|
/* @__PURE__ */ jsx(Text, { bold: true, color: config2.clearScreen ? "green" : "red", children: config2.clearScreen ? "on" : "off" })
|
|
806
956
|
] }),
|
|
957
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
958
|
+
/* @__PURE__ */ jsxs(Text, { color: cursor === 3 ? "green" : void 0, children: [
|
|
959
|
+
cursor === 3 ? "\u25B8" : " ",
|
|
960
|
+
" "
|
|
961
|
+
] }),
|
|
962
|
+
/* @__PURE__ */ jsx(Text, { children: "Timezone " }),
|
|
963
|
+
editing ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
964
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "[" }),
|
|
965
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: tzEdit }),
|
|
966
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "_" }),
|
|
967
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "]" })
|
|
968
|
+
] }) : /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: tzDisplay })
|
|
969
|
+
] }),
|
|
970
|
+
cursor === 3 && tzError && /* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
971
|
+
" ",
|
|
972
|
+
tzError
|
|
973
|
+
] }),
|
|
807
974
|
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
808
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191\u2193 select \u2190\u2192 adjust s/Esc close" })
|
|
975
|
+
editing ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "type IANA name (e.g. Europe/London) \xB7 empty = System \xB7 Enter save \xB7 Esc cancel" }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191\u2193 select \u2190\u2192 adjust Enter edit tz s/Esc close" })
|
|
809
976
|
] });
|
|
810
977
|
}
|
|
811
978
|
function DashboardView({ data, billing }) {
|