tokmon 0.28.1 → 0.28.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.
- package/README.md +156 -234
- package/THIRD_PARTY_NOTICES.md +62 -0
- package/dist/{bootstrap-ink-L7QSJBMS.js → bootstrap-ink-WG4WG3R2.js} +1579 -1135
- package/dist/chunk-7CH6S7BR.js +447 -0
- package/dist/{chunk-CVKCVHS7.js → chunk-CFKUZCMB.js} +70 -310
- package/dist/chunk-HNWEJ6MS.js +1094 -0
- package/dist/chunk-JZSZEHVD.js +406 -0
- package/dist/{chunk-S33XIUAW.js → chunk-P57DVFNH.js} +277 -49
- package/dist/{chunk-4HU4EI5T.js → chunk-P5JN5DDX.js} +25 -46
- package/dist/chunk-TN6V7XSL.js +512 -0
- package/dist/chunk-USKOQIE3.js +278 -0
- package/dist/chunk-UYPDMVW5.js +21 -0
- package/dist/{cli-command-75LP4IDS.js → cli-command-MD3RJRP4.js} +249 -15
- package/dist/cli.js +6 -6
- package/dist/{config-HXFJTNLM.js → config-3DGZ47F7.js} +23 -3
- package/dist/daemon-EMOA36WB.js +377 -0
- package/dist/daemon-handle-JHE5MKMI.js +11 -0
- package/dist/server-EPSH52DR.js +12 -0
- package/dist/web/assets/{Area-CMXvOw33.js → Area-stRnp8Km.js} +1 -1
- package/dist/web/assets/analytics-moZZt9L_.js +2 -0
- package/dist/web/assets/breakdown-km9w7myU.js +4 -0
- package/dist/web/assets/button-DrX04Cc2.js +1 -0
- package/dist/web/assets/{chart-cZoLgpmG.js → chart-D0CtuL2k.js} +1 -1
- package/dist/web/assets/explore-B11uNJV1.js +22 -0
- package/dist/web/assets/index-RDhXsCNG.js +83 -0
- package/dist/web/assets/index-ZmOS8p9O.css +1 -0
- package/dist/web/assets/models-DYbZnY34.js +2 -0
- package/dist/web/assets/overview-CruICbfJ.js +2 -0
- package/dist/web/assets/panel-xckM3_GC.js +1 -0
- package/dist/web/assets/settings-sheet-G-BTSIF6.js +1 -0
- package/dist/web/assets/share-sheet-weIdd4DW.js +2 -0
- package/dist/web/assets/timeline-BHgniliZ.js +1 -0
- package/dist/web/index.html +30 -4
- package/package.json +13 -4
- package/site/THIRD_PARTY_NOTICES.md +83 -0
- package/site/public/fonts/OFL.txt +93 -0
- package/dist/chunk-3RTWFGGD.js +0 -275
- package/dist/chunk-FMP3P2WV.js +0 -299
- package/dist/chunk-HP5UZCXP.js +0 -436
- package/dist/chunk-SMPY52EV.js +0 -125
- package/dist/chunk-XDA5RJST.js +0 -193
- package/dist/daemon-CL4FJW26.js +0 -219
- package/dist/daemon-handle-QQLJE46Y.js +0 -10
- package/dist/server-EUO7CWYH.js +0 -11
- package/dist/web/assets/analytics-DqdZXOJL.js +0 -2
- package/dist/web/assets/breakdown-kOSSJ3mI.js +0 -4
- package/dist/web/assets/explore-vcsM8K9I.js +0 -22
- package/dist/web/assets/index-BErCXT7f.css +0 -1
- package/dist/web/assets/index-DH3dOnbg.js +0 -83
- package/dist/web/assets/models-B5ikm83_.js +0 -2
- package/dist/web/assets/overview-DD1kP8CP.js +0 -2
- package/dist/web/assets/panel-DTQBwExW.js +0 -1
- package/dist/web/assets/primitives-C36qJlA2.js +0 -1
- package/dist/web/assets/settings-sheet-_i2zTQjf.js +0 -1
- package/dist/web/assets/share-sheet-BLHmrFbm.js +0 -2
- package/dist/web/assets/timeline-B4uG-Emo.js +0 -1
- package/dist/web/assets/use-dialog-trap-bfaXEMz9.js +0 -1
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createDaemonRpcClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7CH6S7BR.js";
|
|
5
5
|
import {
|
|
6
|
+
accountIdentityText,
|
|
6
7
|
accountsByProvider,
|
|
7
8
|
buildAccounts,
|
|
8
9
|
fetchPeak,
|
|
9
|
-
modelColor
|
|
10
|
-
|
|
10
|
+
modelColor,
|
|
11
|
+
resolveQuotaViews,
|
|
12
|
+
severity,
|
|
13
|
+
usageFromHeadroom
|
|
14
|
+
} from "./chunk-TN6V7XSL.js";
|
|
11
15
|
import {
|
|
12
16
|
PROVIDERS,
|
|
13
17
|
PROVIDER_ORDER,
|
|
@@ -24,23 +28,33 @@ import {
|
|
|
24
28
|
time,
|
|
25
29
|
tokens,
|
|
26
30
|
withTimeout
|
|
27
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-CFKUZCMB.js";
|
|
32
|
+
import "./chunk-JZSZEHVD.js";
|
|
28
33
|
import {
|
|
29
34
|
COLOR_PALETTE,
|
|
30
35
|
DEFAULTS,
|
|
36
|
+
DESKTOP_GRAPH_RANGES,
|
|
37
|
+
THEME_PRESET_IDS,
|
|
31
38
|
configLocation,
|
|
32
39
|
generateAccountId,
|
|
33
40
|
getTrackedAccountRows,
|
|
41
|
+
isDarkOnlyThemePreset,
|
|
34
42
|
isValidTimezone,
|
|
35
43
|
loadConfig,
|
|
36
44
|
normalizeAllowedHost,
|
|
37
45
|
normalizeConfig,
|
|
38
46
|
pickAccentColor,
|
|
47
|
+
providerDetectionEnabled,
|
|
39
48
|
redactEmail,
|
|
49
|
+
resolveTerminalTheme,
|
|
40
50
|
sanitizeTyped,
|
|
41
51
|
saveConfigSync,
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
setDetectedAccountExcluded,
|
|
53
|
+
setProviderDetectionEnabled,
|
|
54
|
+
snapshotCacheFile,
|
|
55
|
+
themePresetOption,
|
|
56
|
+
toggleProviderSelection
|
|
57
|
+
} from "./chunk-HNWEJ6MS.js";
|
|
44
58
|
import {
|
|
45
59
|
glyphs
|
|
46
60
|
} from "./chunk-RF4GGQGM.js";
|
|
@@ -50,7 +64,7 @@ import { render } from "ink";
|
|
|
50
64
|
import { MouseProvider } from "@zenobius/ink-mouse";
|
|
51
65
|
|
|
52
66
|
// src/app.tsx
|
|
53
|
-
import { useState as useState10, useEffect as useEffect9, useCallback as useCallback7, useRef as useRef8, useMemo as
|
|
67
|
+
import { useState as useState10, useEffect as useEffect9, useCallback as useCallback7, useRef as useRef8, useMemo as useMemo3 } from "react";
|
|
54
68
|
import { Box as Box11, Text as Text12, useInput, useApp } from "ink";
|
|
55
69
|
import { useMouse } from "@zenobius/ink-mouse";
|
|
56
70
|
|
|
@@ -59,7 +73,55 @@ import { appendFileSync } from "fs";
|
|
|
59
73
|
import { memo, useEffect, useRef, useState } from "react";
|
|
60
74
|
import { Box, Text } from "ink";
|
|
61
75
|
import { useOnMouseClick } from "@zenobius/ink-mouse";
|
|
62
|
-
|
|
76
|
+
|
|
77
|
+
// src/ui/theme.tsx
|
|
78
|
+
import { createContext, useContext, useMemo } from "react";
|
|
79
|
+
import { jsx } from "react/jsx-runtime";
|
|
80
|
+
var ANSI_THEME = {
|
|
81
|
+
accent: "greenBright",
|
|
82
|
+
cost: "yellow",
|
|
83
|
+
positive: "green",
|
|
84
|
+
ok: "green",
|
|
85
|
+
warn: "yellow",
|
|
86
|
+
crit: "red",
|
|
87
|
+
unknown: void 0
|
|
88
|
+
};
|
|
89
|
+
var NO_COLOR_THEME = {
|
|
90
|
+
accent: void 0,
|
|
91
|
+
cost: void 0,
|
|
92
|
+
positive: void 0,
|
|
93
|
+
ok: void 0,
|
|
94
|
+
warn: void 0,
|
|
95
|
+
crit: void 0,
|
|
96
|
+
unknown: void 0
|
|
97
|
+
};
|
|
98
|
+
var TuiThemeContext = createContext(ANSI_THEME);
|
|
99
|
+
function fromTokens(tokens2) {
|
|
100
|
+
return {
|
|
101
|
+
accent: tokens2.accent,
|
|
102
|
+
cost: tokens2.cost,
|
|
103
|
+
positive: tokens2.positive,
|
|
104
|
+
ok: tokens2.ok,
|
|
105
|
+
warn: tokens2.warn,
|
|
106
|
+
crit: tokens2.crit,
|
|
107
|
+
unknown: tokens2.unknown
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function resolveTuiTheme(appearance, noColor = process.env.NO_COLOR !== void 0) {
|
|
111
|
+
if (noColor || appearance.terminal === "off") return NO_COLOR_THEME;
|
|
112
|
+
const resolved = resolveTerminalTheme(appearance);
|
|
113
|
+
return resolved ? fromTokens(resolved.tokens) : ANSI_THEME;
|
|
114
|
+
}
|
|
115
|
+
function TuiThemeProvider({ appearance, children }) {
|
|
116
|
+
const value = useMemo(() => resolveTuiTheme(appearance), [appearance]);
|
|
117
|
+
return /* @__PURE__ */ jsx(TuiThemeContext.Provider, { value, children });
|
|
118
|
+
}
|
|
119
|
+
function useTuiTheme() {
|
|
120
|
+
return useContext(TuiThemeContext);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/ui/shared.tsx
|
|
124
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
63
125
|
function truncateName(s, n) {
|
|
64
126
|
const ell = glyphs().ellipsis;
|
|
65
127
|
return s.length > n ? s.slice(0, n - ell.length) + ell : s;
|
|
@@ -69,7 +131,7 @@ function ClickableBox({ onClick, children, ...props }) {
|
|
|
69
131
|
useOnMouseClick(ref, (clicked) => {
|
|
70
132
|
if (clicked) onClick();
|
|
71
133
|
});
|
|
72
|
-
return /* @__PURE__ */
|
|
134
|
+
return /* @__PURE__ */ jsx2(Box, { ref, ...props, children });
|
|
73
135
|
}
|
|
74
136
|
var SGR_PRESS = /\x1b\[<(\d+);(\d+);(\d+)M/g;
|
|
75
137
|
var linkHits = /* @__PURE__ */ new Set();
|
|
@@ -137,9 +199,10 @@ function LinkBox({ onClick, children, ...props }) {
|
|
|
137
199
|
linkHits.delete(hit);
|
|
138
200
|
};
|
|
139
201
|
}, []);
|
|
140
|
-
return /* @__PURE__ */
|
|
202
|
+
return /* @__PURE__ */ jsx2(Box, { ref, ...props, children });
|
|
141
203
|
}
|
|
142
204
|
function Spinner({ label }) {
|
|
205
|
+
const theme = useTuiTheme();
|
|
143
206
|
const frames = glyphs().spinner;
|
|
144
207
|
const [i, setI] = useState(0);
|
|
145
208
|
useEffect(() => {
|
|
@@ -147,15 +210,15 @@ function Spinner({ label }) {
|
|
|
147
210
|
return () => clearInterval(id);
|
|
148
211
|
}, []);
|
|
149
212
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
150
|
-
/* @__PURE__ */ jsxs(Text, { color:
|
|
213
|
+
/* @__PURE__ */ jsxs(Text, { color: theme.accent, children: [
|
|
151
214
|
frames[i],
|
|
152
215
|
" "
|
|
153
216
|
] }),
|
|
154
|
-
/* @__PURE__ */
|
|
217
|
+
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: label })
|
|
155
218
|
] });
|
|
156
219
|
}
|
|
157
220
|
var TabBar = memo(function TabBar2({ tabs, active, onSelect }) {
|
|
158
|
-
return /* @__PURE__ */
|
|
221
|
+
return /* @__PURE__ */ jsx2(Box, { children: tabs.map((t, i) => /* @__PURE__ */ jsx2(ClickableBox, { onClick: () => onSelect(i), marginRight: 1, children: i === active ? /* @__PURE__ */ jsxs(Text, { bold: true, inverse: true, children: [
|
|
159
222
|
" ",
|
|
160
223
|
t,
|
|
161
224
|
" "
|
|
@@ -166,14 +229,15 @@ var TabBar = memo(function TabBar2({ tabs, active, onSelect }) {
|
|
|
166
229
|
] }) }, t)) });
|
|
167
230
|
});
|
|
168
231
|
var PeakBadge = memo(function PeakBadge2({ peak, resetDisplay = "relative", tz }) {
|
|
169
|
-
const
|
|
232
|
+
const theme = useTuiTheme();
|
|
233
|
+
const color = peak.state === "peak" ? theme.crit : theme.ok;
|
|
170
234
|
const changesAt = peak.changesAt ?? (peak.minutesUntilChange !== null ? new Date(Date.now() + peak.minutesUntilChange * 6e4).toISOString() : null);
|
|
171
235
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
172
236
|
/* @__PURE__ */ jsxs(Text, { color, children: [
|
|
173
237
|
glyphs().dot,
|
|
174
238
|
" "
|
|
175
239
|
] }),
|
|
176
|
-
/* @__PURE__ */
|
|
240
|
+
/* @__PURE__ */ jsx2(Text, { bold: true, color, children: peak.label }),
|
|
177
241
|
changesAt && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
178
242
|
" (",
|
|
179
243
|
formatResetAt(changesAt, resetDisplay, Date.now(), tz),
|
|
@@ -198,8 +262,8 @@ function sparkline(values) {
|
|
|
198
262
|
function Bar({ pct, color, width = 24 }) {
|
|
199
263
|
const filled = Math.max(0, Math.min(width, Math.round(pct / 100 * width)));
|
|
200
264
|
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
201
|
-
/* @__PURE__ */
|
|
202
|
-
/* @__PURE__ */
|
|
265
|
+
/* @__PURE__ */ jsx2(Text, { color, children: glyphs().barFull.repeat(filled) }),
|
|
266
|
+
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: glyphs().barEmpty.repeat(width - filled) })
|
|
203
267
|
] });
|
|
204
268
|
}
|
|
205
269
|
function metricValueText(m) {
|
|
@@ -215,6 +279,20 @@ function metricValueText(m) {
|
|
|
215
279
|
}
|
|
216
280
|
return `${Math.round(m.used)}%`;
|
|
217
281
|
}
|
|
282
|
+
function CaretText({ value, caret: caret2, color }) {
|
|
283
|
+
const c = Math.max(0, Math.min(caret2, value.length));
|
|
284
|
+
if (c >= value.length) {
|
|
285
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
286
|
+
/* @__PURE__ */ jsx2(Text, { bold: true, color, children: value }),
|
|
287
|
+
/* @__PURE__ */ jsx2(Text, { color, children: glyphs().vbar })
|
|
288
|
+
] });
|
|
289
|
+
}
|
|
290
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
291
|
+
/* @__PURE__ */ jsx2(Text, { bold: true, color, children: value.slice(0, c) }),
|
|
292
|
+
/* @__PURE__ */ jsx2(Text, { inverse: true, color, children: value[c] }),
|
|
293
|
+
/* @__PURE__ */ jsx2(Text, { bold: true, color, children: value.slice(c + 1) })
|
|
294
|
+
] });
|
|
295
|
+
}
|
|
218
296
|
|
|
219
297
|
// src/ui/dashboard.tsx
|
|
220
298
|
import { memo as memo2 } from "react";
|
|
@@ -243,8 +321,58 @@ function planDisplay(rawPlans) {
|
|
|
243
321
|
return { mode: "perRow", count: plans.length };
|
|
244
322
|
}
|
|
245
323
|
|
|
324
|
+
// src/dashboard-data.ts
|
|
325
|
+
var emptyUsageSummary = () => ({
|
|
326
|
+
cost: 0,
|
|
327
|
+
tokens: 0,
|
|
328
|
+
input: 0,
|
|
329
|
+
cacheRead: 0,
|
|
330
|
+
cacheSavings: 0
|
|
331
|
+
});
|
|
332
|
+
function addUsageSummary(target, source) {
|
|
333
|
+
target.cost += source.cost;
|
|
334
|
+
target.tokens += source.tokens;
|
|
335
|
+
target.input += source.input;
|
|
336
|
+
target.cacheRead += source.cacheRead;
|
|
337
|
+
target.cacheSavings += source.cacheSavings;
|
|
338
|
+
}
|
|
339
|
+
function aggregateDashboardData(snapshots) {
|
|
340
|
+
const dashboards = snapshots.filter((value) => value != null);
|
|
341
|
+
if (dashboards.length === 0) return null;
|
|
342
|
+
const result = {
|
|
343
|
+
today: emptyUsageSummary(),
|
|
344
|
+
week: emptyUsageSummary(),
|
|
345
|
+
month: emptyUsageSummary(),
|
|
346
|
+
burnRate: 0,
|
|
347
|
+
series: Array.from({ length: Math.max(...dashboards.map((value) => value.series.length)) }, () => 0),
|
|
348
|
+
lastActivityAt: null
|
|
349
|
+
};
|
|
350
|
+
for (const dashboard of dashboards) {
|
|
351
|
+
addUsageSummary(result.today, dashboard.today);
|
|
352
|
+
addUsageSummary(result.week, dashboard.week);
|
|
353
|
+
addUsageSummary(result.month, dashboard.month);
|
|
354
|
+
result.burnRate += dashboard.burnRate;
|
|
355
|
+
if (dashboard.lastActivityAt !== null && (result.lastActivityAt === null || dashboard.lastActivityAt > result.lastActivityAt)) {
|
|
356
|
+
result.lastActivityAt = dashboard.lastActivityAt;
|
|
357
|
+
}
|
|
358
|
+
const offset = result.series.length - dashboard.series.length;
|
|
359
|
+
dashboard.series.forEach((value, index) => {
|
|
360
|
+
result.series[offset + index] += value;
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
function cachedTokenPercentage(summary) {
|
|
366
|
+
if (!Number.isFinite(summary.tokens) || !Number.isFinite(summary.cacheRead) || summary.tokens <= 0) return 0;
|
|
367
|
+
return Math.round(Math.min(100, Math.max(0, summary.cacheRead / summary.tokens * 100)));
|
|
368
|
+
}
|
|
369
|
+
|
|
246
370
|
// src/ui/dashboard.tsx
|
|
247
|
-
import { Fragment, jsx as
|
|
371
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
372
|
+
function severityColor(remaining, crit, warn, base) {
|
|
373
|
+
const level = severity(remaining);
|
|
374
|
+
return level === "crit" ? crit : level === "warn" ? warn : base;
|
|
375
|
+
}
|
|
248
376
|
var GAP = 2;
|
|
249
377
|
var MIN_CARD = 56;
|
|
250
378
|
var MIN_CARD_DENSE = 50;
|
|
@@ -262,8 +390,8 @@ function estimateCardHeights(groups, stats) {
|
|
|
262
390
|
const multi = g.accounts.length > 1;
|
|
263
391
|
g.accounts.forEach((a, i) => {
|
|
264
392
|
const s = stats.get(a.id);
|
|
265
|
-
const staleRow = s?.billing && !s.billing.error && s.
|
|
266
|
-
const metricRows = (s
|
|
393
|
+
const staleRow = s?.billing && !s.billing.error && quotaViews(s).length > 0 && billingStaleLabel(s.billing.asOfMs ?? s.billingUpdatedAt, Date.now()) ? 1 : 0;
|
|
394
|
+
const metricRows = (quotaViews(s).length || 1) + staleRow;
|
|
267
395
|
h += metricRows + (multi ? 1 : 0) + (multi && i > 0 ? 1 : 0);
|
|
268
396
|
});
|
|
269
397
|
}
|
|
@@ -318,7 +446,7 @@ function computeDashLayout(groups, stats, cols, budget, focusId, layoutPref) {
|
|
|
318
446
|
}
|
|
319
447
|
return chooseLayout(content, budget, groups.length, single, cols, heights);
|
|
320
448
|
}
|
|
321
|
-
var DashboardView = memo2(function DashboardView2({ groups, stats, cols, budget,
|
|
449
|
+
var DashboardView = memo2(function DashboardView2({ groups, stats, cols, budget, computed, page = 0, privacyMode = false, resetDisplay = "relative", tz }) {
|
|
322
450
|
if (groups.length === 0) {
|
|
323
451
|
return /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
324
452
|
"No providers enabled ",
|
|
@@ -327,13 +455,13 @@ var DashboardView = memo2(function DashboardView2({ groups, stats, cols, budget,
|
|
|
327
455
|
] });
|
|
328
456
|
}
|
|
329
457
|
const content = Math.max(30, cols - 4);
|
|
330
|
-
const { ncols, variant, cardsPerPage, pageCount } =
|
|
458
|
+
const { ncols, variant, cardsPerPage, pageCount } = computed;
|
|
331
459
|
let cardW = ncols <= 1 ? content : Math.floor((content - GAP * (ncols - 1)) / ncols);
|
|
332
460
|
if (ncols === 1 && cardW > MAX_SINGLE_CARD) cardW = MAX_SINGLE_CARD;
|
|
333
461
|
const pg = pageCount > 1 ? (page % pageCount + pageCount) % pageCount : 0;
|
|
334
462
|
const visible = pageCount > 1 ? groups.slice(pg * cardsPerPage, pg * cardsPerPage + cardsPerPage) : groups;
|
|
335
463
|
return /* @__PURE__ */ jsxs2(Box2, { height: budget, flexDirection: "column", overflow: "hidden", children: [
|
|
336
|
-
/* @__PURE__ */
|
|
464
|
+
/* @__PURE__ */ jsx3(Box2, { width: content, flexWrap: "wrap", columnGap: GAP, rowGap: 1, alignItems: "flex-start", children: visible.map((g) => /* @__PURE__ */ jsx3(Box2, { flexShrink: 0, children: /* @__PURE__ */ jsx3(ProviderCard, { provider: g.provider, accounts: g.accounts, stats, width: cardW, variant, privacyMode, resetDisplay, tz }) }, g.provider)) }),
|
|
337
465
|
pageCount > 1 && /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
338
466
|
" ",
|
|
339
467
|
glyphs().middot,
|
|
@@ -350,127 +478,143 @@ var DashboardView = memo2(function DashboardView2({ groups, stats, cols, budget,
|
|
|
350
478
|
] });
|
|
351
479
|
});
|
|
352
480
|
function ProviderCard({ provider, accounts, stats, width, variant, privacyMode = false, resetDisplay, tz }) {
|
|
481
|
+
const theme = useTuiTheme();
|
|
353
482
|
const meta = PROVIDERS[provider];
|
|
354
483
|
const items = accounts.map((a) => ({ account: a, s: stats.get(a.id) }));
|
|
355
|
-
const
|
|
356
|
-
const agg = meta.hasUsage && dashboards.length > 0 ? aggregate(dashboards) : null;
|
|
484
|
+
const agg = meta.hasUsage ? aggregateDashboardData(items.map((i) => i.s?.dashboard)) : null;
|
|
357
485
|
const planView = planDisplay(items.map((i) => i.s?.billing?.plan));
|
|
358
486
|
const activity = items.map((i) => i.s?.billing?.activity).find(Boolean) ?? null;
|
|
359
487
|
const inner = width - 4;
|
|
360
488
|
const hasSpark = !!agg && agg.series.some((v) => v > 0);
|
|
361
489
|
const showBars = variant !== "mini";
|
|
362
490
|
const showSpark = variant === "full";
|
|
491
|
+
const headroom = items.find((item) => item.s?.providerHeadroom)?.s?.providerHeadroom;
|
|
363
492
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width, borderStyle: glyphs().border, borderColor: meta.color, paddingX: 1, children: [
|
|
364
493
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
365
494
|
/* @__PURE__ */ jsxs2(Text2, { color: meta.color, children: [
|
|
366
495
|
glyphs().dot,
|
|
367
496
|
" "
|
|
368
497
|
] }),
|
|
369
|
-
/* @__PURE__ */
|
|
370
|
-
/* @__PURE__ */
|
|
371
|
-
planView.mode === "header" && /* @__PURE__ */
|
|
498
|
+
/* @__PURE__ */ jsx3(Text2, { bold: true, color: meta.color, children: meta.name }),
|
|
499
|
+
/* @__PURE__ */ jsx3(Box2, { flexGrow: 1 }),
|
|
500
|
+
planView.mode === "header" && /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: planView.plan }),
|
|
372
501
|
planView.mode === "perRow" && /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
373
502
|
planView.count,
|
|
374
503
|
" accounts"
|
|
375
504
|
] })
|
|
376
505
|
] }),
|
|
377
|
-
|
|
378
|
-
/* @__PURE__ */
|
|
379
|
-
/* @__PURE__ */
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
] })
|
|
384
|
-
|
|
506
|
+
headroom?.value != null && /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
507
|
+
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "Usage " }),
|
|
508
|
+
/* @__PURE__ */ jsxs2(Text2, { bold: true, color: severityColor(headroom.value, theme.crit, theme.warn, meta.color), children: [
|
|
509
|
+
Math.round(usageFromHeadroom(headroom.value)),
|
|
510
|
+
"%"
|
|
511
|
+
] })
|
|
512
|
+
] }),
|
|
513
|
+
meta.hasUsage && (agg ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
514
|
+
/* @__PURE__ */ jsx3(Box2, { height: 1 }),
|
|
515
|
+
/* @__PURE__ */ jsx3(SummaryRow, { label: "Today", s: agg.today }),
|
|
516
|
+
/* @__PURE__ */ jsx3(SummaryRow, { label: "This Week", s: agg.week }),
|
|
517
|
+
/* @__PURE__ */ jsx3(SummaryRow, { label: "This Month", s: agg.month }),
|
|
518
|
+
/* @__PURE__ */ jsx3(KpiLine, { agg })
|
|
519
|
+
] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
520
|
+
/* @__PURE__ */ jsx3(Box2, { height: 1 }),
|
|
385
521
|
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
386
522
|
"Fetching usage",
|
|
387
523
|
glyphs().ellipsis
|
|
388
524
|
] })
|
|
389
525
|
] })),
|
|
390
|
-
meta.hasBilling && showBars && /* @__PURE__ */ jsxs2(
|
|
391
|
-
meta.hasUsage && /* @__PURE__ */
|
|
392
|
-
/* @__PURE__ */
|
|
526
|
+
meta.hasBilling && showBars && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
527
|
+
meta.hasUsage && /* @__PURE__ */ jsx3(Rule, { inner }),
|
|
528
|
+
/* @__PURE__ */ jsx3(LimitsBlock, { items, inner, showRowPlans: planView.mode === "perRow", privacyMode, resetDisplay, tz, providerName: meta.name })
|
|
393
529
|
] }),
|
|
394
|
-
meta.hasBilling && !showBars && !meta.hasUsage && /* @__PURE__ */
|
|
395
|
-
hasSpark && showSpark && /* @__PURE__ */ jsxs2(
|
|
396
|
-
/* @__PURE__ */
|
|
397
|
-
/* @__PURE__ */
|
|
530
|
+
meta.hasBilling && !showBars && !meta.hasUsage && /* @__PURE__ */ jsx3(CompactBilling, { items, privacyMode }),
|
|
531
|
+
hasSpark && showSpark && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
532
|
+
/* @__PURE__ */ jsx3(Rule, { inner }),
|
|
533
|
+
/* @__PURE__ */ jsx3(SparkFooter, { series: agg.series, month: agg.month.cost, color: meta.color })
|
|
398
534
|
] }),
|
|
399
|
-
!meta.hasUsage && activity && showSpark && /* @__PURE__ */ jsxs2(
|
|
400
|
-
/* @__PURE__ */
|
|
535
|
+
!meta.hasUsage && activity && showSpark && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
536
|
+
/* @__PURE__ */ jsx3(Rule, { inner }),
|
|
401
537
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
402
|
-
/* @__PURE__ */
|
|
403
|
-
/* @__PURE__ */
|
|
404
|
-
/* @__PURE__ */
|
|
538
|
+
/* @__PURE__ */ jsx3(Box2, { width: 4, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "14d" }) }),
|
|
539
|
+
/* @__PURE__ */ jsx3(Text2, { color: meta.color, children: sparkline(activity.series.slice(-14)) }),
|
|
540
|
+
/* @__PURE__ */ jsx3(Box2, { flexGrow: 1, justifyContent: "flex-end", children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: activity.summary }) })
|
|
405
541
|
] })
|
|
406
542
|
] })
|
|
407
543
|
] });
|
|
408
544
|
}
|
|
409
545
|
function CompactBilling({ items, privacyMode }) {
|
|
410
|
-
const
|
|
546
|
+
const theme = useTuiTheme();
|
|
547
|
+
const stats = items.map((item) => item.s).find((value) => value?.billing);
|
|
548
|
+
const billing = stats?.billing;
|
|
411
549
|
if (!billing) return /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
412
550
|
"Fetching",
|
|
413
551
|
glyphs().ellipsis
|
|
414
552
|
] });
|
|
415
|
-
if (billing.error) return /* @__PURE__ */
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
return /* @__PURE__ */
|
|
553
|
+
if (billing.error) return /* @__PURE__ */ jsx3(Text2, { color: theme.crit, children: privacyMode ? redactEmail(billing.error) : billing.error });
|
|
554
|
+
const quotas = quotaViews(stats);
|
|
555
|
+
const quota = quotas.find((value) => value.primary) ?? quotas[0];
|
|
556
|
+
if (!quota) return /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "No data" });
|
|
557
|
+
return /* @__PURE__ */ jsx3(Text2, { bold: true, color: theme.cost, children: quota.valueText });
|
|
419
558
|
}
|
|
420
559
|
function Rule({ inner }) {
|
|
421
|
-
return /* @__PURE__ */
|
|
560
|
+
return /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: glyphs().rule.repeat(Math.max(0, inner)) });
|
|
422
561
|
}
|
|
423
562
|
function SummaryRow({ label, s }) {
|
|
424
|
-
const
|
|
563
|
+
const theme = useTuiTheme();
|
|
564
|
+
const cachedPct = cachedTokenPercentage(s);
|
|
425
565
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
426
|
-
/* @__PURE__ */
|
|
427
|
-
/* @__PURE__ */
|
|
428
|
-
/* @__PURE__ */
|
|
566
|
+
/* @__PURE__ */ jsx3(Box2, { width: 11, flexShrink: 0, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, wrap: "truncate", children: label }) }),
|
|
567
|
+
/* @__PURE__ */ jsx3(Box2, { width: 11, flexShrink: 0, justifyContent: "flex-end", children: /* @__PURE__ */ jsx3(Text2, { bold: true, color: theme.cost, wrap: "truncate", children: currency(s.cost) }) }),
|
|
568
|
+
/* @__PURE__ */ jsx3(Box2, { width: 13, flexShrink: 0, justifyContent: "flex-end", children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, wrap: "truncate", children: [
|
|
429
569
|
tokens(s.tokens),
|
|
430
570
|
" tok"
|
|
431
571
|
] }) }),
|
|
432
|
-
/* @__PURE__ */
|
|
572
|
+
/* @__PURE__ */ jsx3(Box2, { flexGrow: 1, justifyContent: "flex-end", children: cachedPct > 0 ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, wrap: "truncate", children: [
|
|
433
573
|
cachedPct,
|
|
434
574
|
"% cached"
|
|
435
|
-
] }) : /* @__PURE__ */
|
|
575
|
+
] }) : /* @__PURE__ */ jsx3(Text2, { children: " " }) })
|
|
436
576
|
] });
|
|
437
577
|
}
|
|
438
578
|
function KpiLine({ agg }) {
|
|
579
|
+
const theme = useTuiTheme();
|
|
439
580
|
const hasBurn = agg.burnRate > 0;
|
|
440
581
|
const hasSaved = agg.month.cacheSavings > 0;
|
|
441
582
|
if (!hasBurn && !hasSaved) return null;
|
|
442
583
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
443
|
-
hasBurn && /* @__PURE__ */ jsxs2(
|
|
444
|
-
/* @__PURE__ */
|
|
445
|
-
/* @__PURE__ */ jsxs2(Text2, { color:
|
|
584
|
+
hasBurn && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
585
|
+
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "Burn " }),
|
|
586
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.crit, children: [
|
|
446
587
|
currency(agg.burnRate),
|
|
447
588
|
"/hr"
|
|
448
589
|
] })
|
|
449
590
|
] }),
|
|
450
|
-
/* @__PURE__ */
|
|
451
|
-
hasSaved && /* @__PURE__ */ jsxs2(
|
|
452
|
-
/* @__PURE__ */
|
|
453
|
-
/* @__PURE__ */ jsxs2(Text2, { color:
|
|
591
|
+
/* @__PURE__ */ jsx3(Box2, { flexGrow: 1 }),
|
|
592
|
+
hasSaved && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
593
|
+
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "Cache saved " }),
|
|
594
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.positive, children: [
|
|
454
595
|
currency(agg.month.cacheSavings),
|
|
455
596
|
"/mo"
|
|
456
597
|
] })
|
|
457
598
|
] })
|
|
458
599
|
] });
|
|
459
600
|
}
|
|
460
|
-
function accountTitle(account, billing, privacyMode = false) {
|
|
601
|
+
function accountTitle(account, billing, privacyMode = false, identity, providerName = "") {
|
|
602
|
+
if (identity) return accountIdentityText({ identity, name: account.name }, providerName);
|
|
461
603
|
const email = billing?.email;
|
|
462
604
|
const title = email && !account.name.includes("@") ? `${account.name} ${email}` : account.name;
|
|
463
605
|
return privacyMode ? redactEmail(title) : title;
|
|
464
606
|
}
|
|
465
|
-
function LimitsBlock({ items, inner, showRowPlans, privacyMode, resetDisplay, tz }) {
|
|
607
|
+
function LimitsBlock({ items, inner, showRowPlans, privacyMode, resetDisplay, tz, providerName }) {
|
|
608
|
+
const theme = useTuiTheme();
|
|
466
609
|
const showName = items.length > 1;
|
|
467
|
-
const labels = items.flatMap((i) => i.s
|
|
610
|
+
const labels = items.flatMap((i) => quotaViews(i.s)).map((quota) => quota.label.length);
|
|
468
611
|
const labelW = Math.min(Math.max(7, ...labels) + 1, 14);
|
|
469
612
|
const resetW = resetDisplay === "absolute" ? 17 : 8;
|
|
470
613
|
const barW = Math.max(10, Math.min(46, inner - labelW - resetW - 5));
|
|
471
|
-
return /* @__PURE__ */
|
|
614
|
+
return /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", children: items.map(({ account, s }, idx) => {
|
|
472
615
|
const billing = s?.billing;
|
|
473
|
-
const
|
|
616
|
+
const quotas = quotaViews(s);
|
|
617
|
+
const staleLabel = billing && !billing.error && quotas.length > 0 ? billingStaleLabel(billing.asOfMs ?? s?.billingUpdatedAt, Date.now()) : null;
|
|
474
618
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: showName && idx > 0 ? 1 : 0, children: [
|
|
475
619
|
showName && (() => {
|
|
476
620
|
const rowPlan = showRowPlans ? normalizePlan(billing?.plan) : null;
|
|
@@ -483,18 +627,18 @@ function LimitsBlock({ items, inner, showRowPlans, privacyMode, resetDisplay, tz
|
|
|
483
627
|
glyphs().dot,
|
|
484
628
|
" "
|
|
485
629
|
] }),
|
|
486
|
-
/* @__PURE__ */
|
|
487
|
-
shownPlan && /* @__PURE__ */ jsxs2(
|
|
488
|
-
/* @__PURE__ */
|
|
489
|
-
/* @__PURE__ */
|
|
630
|
+
/* @__PURE__ */ jsx3(Text2, { bold: true, children: truncateName(accountTitle(account, billing, privacyMode, s?.identity, providerName), nameBudget) }),
|
|
631
|
+
shownPlan && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
632
|
+
/* @__PURE__ */ jsx3(Box2, { flexGrow: 1 }),
|
|
633
|
+
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: shownPlan })
|
|
490
634
|
] })
|
|
491
635
|
] });
|
|
492
636
|
})(),
|
|
493
637
|
!billing ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
494
638
|
"Fetching",
|
|
495
639
|
glyphs().ellipsis
|
|
496
|
-
] }) : billing.error ? /* @__PURE__ */
|
|
497
|
-
staleLabel && /* @__PURE__ */ jsxs2(Text2, { color:
|
|
640
|
+
] }) : billing.error ? /* @__PURE__ */ jsx3(Text2, { color: theme.crit, wrap: "truncate-end", children: privacyMode ? redactEmail(billing.error) : billing.error }) : quotas.length === 0 ? /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "No data" }) : quotas.map((quota) => /* @__PURE__ */ jsx3(QuotaRow, { quota, color: account.color, barW, labelW, resetW, resetDisplay, tz }, quota.key)),
|
|
641
|
+
staleLabel && /* @__PURE__ */ jsxs2(Text2, { color: theme.warn, dimColor: true, children: [
|
|
498
642
|
glyphs().warn,
|
|
499
643
|
" ",
|
|
500
644
|
staleLabel,
|
|
@@ -506,71 +650,45 @@ function LimitsBlock({ items, inner, showRowPlans, privacyMode, resetDisplay, tz
|
|
|
506
650
|
] }, account.id);
|
|
507
651
|
}) });
|
|
508
652
|
}
|
|
509
|
-
function
|
|
510
|
-
|
|
511
|
-
|
|
653
|
+
function quotaViews(stats) {
|
|
654
|
+
return resolveQuotaViews({ quotas: stats?.quotas, metrics: stats?.billing?.metrics });
|
|
655
|
+
}
|
|
656
|
+
function QuotaRow({ quota, color, barW, labelW, resetW, resetDisplay, tz }) {
|
|
657
|
+
const theme = useTuiTheme();
|
|
658
|
+
if (quota.bounded && quota.usedPct !== null) {
|
|
659
|
+
const used = quota.usedPct;
|
|
660
|
+
const remaining = quota.remainingPct ?? 0;
|
|
661
|
+
const barColor = severityColor(remaining, theme.crit, theme.warn, color);
|
|
512
662
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
513
|
-
/* @__PURE__ */
|
|
514
|
-
/* @__PURE__ */
|
|
515
|
-
/* @__PURE__ */
|
|
516
|
-
Math.round(
|
|
663
|
+
/* @__PURE__ */ jsx3(Box2, { width: labelW, flexShrink: 0, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, wrap: "truncate", children: quota.label }) }),
|
|
664
|
+
/* @__PURE__ */ jsx3(Bar, { pct: used, color: barColor, width: barW }),
|
|
665
|
+
/* @__PURE__ */ jsx3(Box2, { width: 5, justifyContent: "flex-end", children: /* @__PURE__ */ jsxs2(Text2, { bold: true, children: [
|
|
666
|
+
Math.round(used),
|
|
517
667
|
"%"
|
|
518
668
|
] }) }),
|
|
519
|
-
/* @__PURE__ */
|
|
669
|
+
/* @__PURE__ */ jsx3(Box2, { width: resetW, justifyContent: "flex-end", children: quota.resetsAt ? /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: resetAt(new Date(quota.resetsAt).toISOString(), resetDisplay, Date.now(), tz) }) : /* @__PURE__ */ jsx3(Text2, { children: " " }) })
|
|
520
670
|
] });
|
|
521
671
|
}
|
|
522
672
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
523
|
-
/* @__PURE__ */
|
|
524
|
-
/* @__PURE__ */
|
|
673
|
+
/* @__PURE__ */ jsx3(Box2, { width: labelW, flexShrink: 0, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, wrap: "truncate", children: quota.label }) }),
|
|
674
|
+
/* @__PURE__ */ jsx3(Text2, { bold: true, color: theme.cost, children: quota.valueText })
|
|
525
675
|
] });
|
|
526
676
|
}
|
|
527
677
|
function SparkFooter({ series, month, color }) {
|
|
528
678
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
529
|
-
/* @__PURE__ */
|
|
530
|
-
/* @__PURE__ */
|
|
531
|
-
/* @__PURE__ */
|
|
679
|
+
/* @__PURE__ */ jsx3(Box2, { width: 4, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "14d" }) }),
|
|
680
|
+
/* @__PURE__ */ jsx3(Text2, { color, children: sparkline(series.slice(-14)) }),
|
|
681
|
+
/* @__PURE__ */ jsx3(Box2, { flexGrow: 1, justifyContent: "flex-end", children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
532
682
|
currency(month),
|
|
533
683
|
" mo"
|
|
534
684
|
] }) })
|
|
535
685
|
] });
|
|
536
686
|
}
|
|
537
|
-
function aggregate(list) {
|
|
538
|
-
const zero = () => ({ cost: 0, tokens: 0, input: 0, cacheRead: 0, cacheSavings: 0 });
|
|
539
|
-
const z = { today: zero(), week: zero(), month: zero(), burnRate: 0, series: [] };
|
|
540
|
-
for (const d of list) {
|
|
541
|
-
for (const k of ["today", "week", "month"]) {
|
|
542
|
-
z[k].cost += d[k].cost;
|
|
543
|
-
z[k].tokens += d[k].tokens;
|
|
544
|
-
z[k].input += d[k].input ?? 0;
|
|
545
|
-
z[k].cacheRead += d[k].cacheRead;
|
|
546
|
-
z[k].cacheSavings += d[k].cacheSavings;
|
|
547
|
-
}
|
|
548
|
-
z.burnRate += d.burnRate;
|
|
549
|
-
d.series.forEach((v, i) => {
|
|
550
|
-
z.series[i] = (z.series[i] ?? 0) + v;
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
return z;
|
|
554
|
-
}
|
|
555
687
|
var TotalsRow = memo2(function TotalsRow2({ groups, stats, cols }) {
|
|
556
|
-
const
|
|
557
|
-
const t =
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
for (const a of g.accounts) {
|
|
561
|
-
const d = stats.get(a.id)?.dashboard;
|
|
562
|
-
if (!d) continue;
|
|
563
|
-
t.cost += d.today.cost;
|
|
564
|
-
t.tokens += d.today.tokens;
|
|
565
|
-
t.input += d.today.input ?? 0;
|
|
566
|
-
w.cost += d.week.cost;
|
|
567
|
-
w.tokens += d.week.tokens;
|
|
568
|
-
w.input += d.week.input ?? 0;
|
|
569
|
-
m.cost += d.month.cost;
|
|
570
|
-
m.tokens += d.month.tokens;
|
|
571
|
-
m.input += d.month.input ?? 0;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
688
|
+
const aggregate = aggregateDashboardData(groups.flatMap((group) => PROVIDERS[group.provider].hasUsage ? group.accounts.map((account) => stats.get(account.id)?.dashboard) : []));
|
|
689
|
+
const t = aggregate?.today ?? { cost: 0, tokens: 0, input: 0, cacheRead: 0, cacheSavings: 0 };
|
|
690
|
+
const w = aggregate?.week ?? { cost: 0, tokens: 0, input: 0, cacheRead: 0, cacheSavings: 0 };
|
|
691
|
+
const m = aggregate?.month ?? { cost: 0, tokens: 0, input: 0, cacheRead: 0, cacheSavings: 0 };
|
|
574
692
|
const inner = cols - 4;
|
|
575
693
|
const dot = glyphs().middot;
|
|
576
694
|
const monthNonCached = m.input > 0 ? `, ${tokens(m.input)} non-cached` : "";
|
|
@@ -579,920 +697,1219 @@ var TotalsRow = memo2(function TotalsRow2({ groups, stats, cols }) {
|
|
|
579
697
|
const noTok = `${glyphs().dotAll} Today ${currency(t.cost)} ${dot} Week ${currency(w.cost)} ${dot} Month ${currency(m.cost)}${m.input > 0 ? ` (${tokens(m.input)} non-cached)` : ""}`;
|
|
580
698
|
const tight = `${glyphs().dotAll} ${currency(t.cost)} ${dot} ${currency(w.cost)} ${dot} ${currency(m.cost)}`;
|
|
581
699
|
const text = full.length <= inner ? full : monthOnly.length <= inner ? monthOnly : noTok.length <= inner ? noTok : tight;
|
|
582
|
-
return /* @__PURE__ */
|
|
700
|
+
return /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: text }) });
|
|
583
701
|
});
|
|
584
702
|
|
|
585
703
|
// src/ui/table.tsx
|
|
586
|
-
import { memo as memo4 } from "react";
|
|
587
|
-
import { Box as Box4, Text as Text4 } from "ink";
|
|
588
|
-
|
|
589
|
-
// src/ui/settings.tsx
|
|
590
704
|
import { memo as memo3 } from "react";
|
|
591
705
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
592
|
-
import { Fragment as
|
|
593
|
-
var
|
|
594
|
-
var
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
/* @__PURE__ */
|
|
624
|
-
/* @__PURE__ */
|
|
625
|
-
/* @__PURE__ */
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
/* @__PURE__ */
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
677
|
-
" ",
|
|
678
|
-
glyphs().caretR
|
|
679
|
-
] })
|
|
680
|
-
] }),
|
|
681
|
-
/* @__PURE__ */ jsxs3(Row, { cursor, idx: 7, label: "Default focus", children: [
|
|
682
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
683
|
-
glyphs().caretL,
|
|
684
|
-
" "
|
|
685
|
-
] }),
|
|
686
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "yellow", children: config.defaultFocus === "all" ? "All" : "Last account" }),
|
|
687
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
688
|
-
" ",
|
|
689
|
-
glyphs().caretR
|
|
690
|
-
] })
|
|
691
|
-
] }),
|
|
692
|
-
/* @__PURE__ */ jsx3(Row, { cursor, idx: 8, label: "Network access", children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: config.allowNetworkAccess ? "red" : "green", children: config.allowNetworkAccess ? "LAN (unsafe)" : "local only" }) }),
|
|
693
|
-
/* @__PURE__ */ jsx3(Row, { cursor, idx: 9, label: "Allowed hosts", children: editingAllowedHosts ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
694
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "[" }),
|
|
695
|
-
/* @__PURE__ */ jsx3(CaretText, { value: allowedHostsEdit ?? "", caret: allowedHostsCaret, color: "cyan" }),
|
|
696
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "]" })
|
|
697
|
-
] }) : /* @__PURE__ */ jsx3(Text3, { bold: true, color: "yellow", children: config.allowedHosts.join(", ") || "none" }) }),
|
|
698
|
-
cursor === 9 && allowedHostsError && /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
|
|
706
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
707
|
+
var MONTHS = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
708
|
+
var TableProviderBar = memo3(function TableProviderBar2({ providers, active, onSelect }) {
|
|
709
|
+
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
710
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "provider " }),
|
|
711
|
+
providers.map((p) => {
|
|
712
|
+
const meta = PROVIDERS[p];
|
|
713
|
+
return /* @__PURE__ */ jsx4(ClickableBox, { onClick: () => onSelect(p), marginRight: 1, children: p === active ? /* @__PURE__ */ jsxs3(Text3, { bold: true, color: meta.color, inverse: true, children: [
|
|
714
|
+
" ",
|
|
715
|
+
meta.name,
|
|
716
|
+
" "
|
|
717
|
+
] }) : /* @__PURE__ */ jsxs3(Text3, { color: meta.color, dimColor: true, children: [
|
|
718
|
+
" ",
|
|
719
|
+
meta.name,
|
|
720
|
+
" "
|
|
721
|
+
] }) }, p);
|
|
722
|
+
}),
|
|
723
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: " p/P switch" })
|
|
724
|
+
] });
|
|
725
|
+
});
|
|
726
|
+
var ControlBar = memo3(function ControlBar2({ views, period, sort, model, search, searchCaret, searching, showPeriod, showModel }) {
|
|
727
|
+
const theme = useTuiTheme();
|
|
728
|
+
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
729
|
+
showPeriod && /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
730
|
+
views.map((v, i) => /* @__PURE__ */ jsx4(Box3, { marginRight: 2, children: i === period ? /* @__PURE__ */ jsxs3(Text3, { bold: true, color: theme.accent, children: [
|
|
731
|
+
"[",
|
|
732
|
+
v,
|
|
733
|
+
"]"
|
|
734
|
+
] }) : /* @__PURE__ */ jsx4(Text3, { dimColor: true, children: v }) }, v)),
|
|
735
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: " " })
|
|
736
|
+
] }),
|
|
737
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "sort " }),
|
|
738
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.accent, children: sort }),
|
|
739
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
740
|
+
" o cycle ",
|
|
741
|
+
glyphs().middot,
|
|
742
|
+
" "
|
|
743
|
+
] }),
|
|
744
|
+
showModel && /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
745
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "model " }),
|
|
746
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, color: model ? modelColor(model) : theme.accent, children: model ?? "all" }),
|
|
747
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
748
|
+
" m model ",
|
|
749
|
+
glyphs().middot,
|
|
750
|
+
" "
|
|
751
|
+
] })
|
|
752
|
+
] }),
|
|
753
|
+
searching ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
754
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "/" }),
|
|
755
|
+
/* @__PURE__ */ jsx4(CaretText, { value: search, caret: searchCaret, color: theme.accent })
|
|
756
|
+
] }) : search ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
757
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "filter " }),
|
|
758
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.positive, children: search }),
|
|
759
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
760
|
+
" (/ edit ",
|
|
761
|
+
glyphs().middot,
|
|
762
|
+
" esc clear)"
|
|
763
|
+
] })
|
|
764
|
+
] }) : /* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "/ filter" })
|
|
765
|
+
] });
|
|
766
|
+
});
|
|
767
|
+
var TokenTable = memo3(function TokenTable2({ rows, cursor, expanded, maxRows, cols, onRowClick }) {
|
|
768
|
+
const theme = useTuiTheme();
|
|
769
|
+
if (rows.length === 0) return /* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "No usage in this period (or filtered out)." });
|
|
770
|
+
const wide = cols > 90;
|
|
771
|
+
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 };
|
|
772
|
+
const fixed = base.label + base.input + base.output + base.cc + base.cr + base.total + base.cost;
|
|
773
|
+
const available = cols - fixed - 6;
|
|
774
|
+
const W = { ...base, models: Math.max(wide ? 22 : 14, available) };
|
|
775
|
+
const lineW = W.label + W.models + W.input + W.output + W.cc + W.cr + W.total + W.cost;
|
|
776
|
+
const totals = { input: 0, output: 0, cacheCreate: 0, cacheRead: 0, cost: 0 };
|
|
777
|
+
for (const r of rows) {
|
|
778
|
+
totals.input += r.input;
|
|
779
|
+
totals.output += r.output;
|
|
780
|
+
totals.cacheCreate += r.cacheCreate;
|
|
781
|
+
totals.cacheRead += r.cacheRead;
|
|
782
|
+
totals.cost += r.cost;
|
|
783
|
+
}
|
|
784
|
+
const clampedCursor = Math.min(cursor, rows.length - 1);
|
|
785
|
+
const scrollStart = Math.max(0, Math.min(clampedCursor - Math.floor(maxRows / 2), rows.length - maxRows));
|
|
786
|
+
const visible = rows.slice(scrollStart, scrollStart + maxRows);
|
|
787
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
788
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
789
|
+
/* @__PURE__ */ jsxs3(Text3, { bold: true, children: [
|
|
699
790
|
" ",
|
|
700
|
-
|
|
791
|
+
col("Date", W.label, "left")
|
|
701
792
|
] }),
|
|
702
|
-
|
|
703
|
-
/* @__PURE__ */
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
710
|
-
" ",
|
|
711
|
-
glyphs().caretR
|
|
712
|
-
] })
|
|
713
|
-
] })
|
|
793
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, children: col("Models", W.models, "left") }),
|
|
794
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, children: col("Input", W.input) }),
|
|
795
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, children: col("Output", W.output) }),
|
|
796
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, children: col(wide ? "Cache Create" : "CchCrt", W.cc) }),
|
|
797
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, children: col(wide ? "Cache Read" : "CchRd", W.cr) }),
|
|
798
|
+
W.total > 0 && /* @__PURE__ */ jsx4(Text3, { bold: true, children: col("Total", W.total) }),
|
|
799
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, children: col("Cost", W.cost) })
|
|
714
800
|
] }),
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
" "
|
|
725
|
-
] }),
|
|
726
|
-
/* @__PURE__ */ jsx3(Text3, { bold: enabled, color: enabled ? p.color : void 0, dimColor: !enabled, children: enabled ? `[${glyphs().check}]` : "[ ]" }),
|
|
727
|
-
/* @__PURE__ */ jsxs3(Text3, { color: p.color, children: [
|
|
728
|
-
" ",
|
|
729
|
-
glyphs().dot,
|
|
730
|
-
" "
|
|
801
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: glyphs().rule.repeat(lineW + 2) }),
|
|
802
|
+
visible.map((r, vi) => {
|
|
803
|
+
const idx = scrollStart + vi;
|
|
804
|
+
const selected = idx === clampedCursor;
|
|
805
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
806
|
+
/* @__PURE__ */ jsx4(ClickableBox, { onClick: () => onRowClick(idx), children: /* @__PURE__ */ jsxs3(Text3, { inverse: selected, children: [
|
|
807
|
+
/* @__PURE__ */ jsxs3(Text3, { color: selected ? void 0 : theme.accent, children: [
|
|
808
|
+
selected ? `${glyphs().caretR} ` : " ",
|
|
809
|
+
col(fmtLabel(r.label), W.label, "left")
|
|
731
810
|
] }),
|
|
732
|
-
/* @__PURE__ */
|
|
733
|
-
/* @__PURE__ */
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
811
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: !selected, children: col(r.models.join(", "), W.models, "left") }),
|
|
812
|
+
/* @__PURE__ */ jsx4(Text3, { children: col(tokens(r.input), W.input) }),
|
|
813
|
+
/* @__PURE__ */ jsx4(Text3, { children: col(tokens(r.output), W.output) }),
|
|
814
|
+
/* @__PURE__ */ jsx4(Text3, { children: col(tokens(r.cacheCreate), W.cc) }),
|
|
815
|
+
/* @__PURE__ */ jsx4(Text3, { children: col(tokens(r.cacheRead), W.cr) }),
|
|
816
|
+
W.total > 0 && /* @__PURE__ */ jsx4(Text3, { children: col(tokens(r.total), W.total) }),
|
|
817
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, color: selected ? void 0 : theme.cost, children: col(currency(r.cost), W.cost) })
|
|
818
|
+
] }) }),
|
|
819
|
+
idx === expanded && /* @__PURE__ */ jsx4(RowDetail, { row: r, indent: W.label + 2 })
|
|
820
|
+
] }, r.label);
|
|
821
|
+
}),
|
|
822
|
+
/* @__PURE__ */ jsx4(Text3, { dimColor: true, children: glyphs().rule.repeat(lineW + 2) }),
|
|
823
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
824
|
+
/* @__PURE__ */ jsxs3(Text3, { bold: true, color: theme.accent, children: [
|
|
825
|
+
" ",
|
|
826
|
+
col("Total", W.label, "left")
|
|
743
827
|
] }),
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
const plan = identity?.plan ?? null;
|
|
752
|
-
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
753
|
-
/* @__PURE__ */ jsxs3(Text3, { color: selected ? "green" : void 0, children: [
|
|
754
|
-
selected ? glyphs().caretR : " ",
|
|
755
|
-
" "
|
|
756
|
-
] }),
|
|
757
|
-
/* @__PURE__ */ jsxs3(Text3, { color: acc.color || provider.color, children: [
|
|
758
|
-
isActive ? glyphs().dot : glyphs().radioOff,
|
|
759
|
-
" "
|
|
760
|
-
] }),
|
|
761
|
-
/* @__PURE__ */ jsx3(Box3, { width: 28, children: /* @__PURE__ */ jsx3(Text3, { bold: true, children: truncateName(identityLabel, 27) }) }),
|
|
762
|
-
/* @__PURE__ */ jsx3(Box3, { width: 9, children: /* @__PURE__ */ jsx3(Text3, { color: provider.color, children: provider.name }) }),
|
|
763
|
-
/* @__PURE__ */ jsx3(Box3, { width: 18, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: plan ? truncateName(plan, 17) : "" }) }),
|
|
764
|
-
/* @__PURE__ */ jsx3(Box3, { width: 12, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: acc.source === "auto" ? "auto tracking" : "configured" }) }),
|
|
765
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: truncateName(acc.homeDir, 24) })
|
|
766
|
-
] }, `${acc.source}:${acc.id}`);
|
|
767
|
-
}),
|
|
768
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
769
|
-
/* @__PURE__ */ jsxs3(Text3, { color: cursor === trackedAccounts.length ? "green" : void 0, children: [
|
|
770
|
-
cursor === trackedAccounts.length ? glyphs().caretR : " ",
|
|
771
|
-
" "
|
|
772
|
-
] }),
|
|
773
|
-
/* @__PURE__ */ jsx3(Text3, { color: "greenBright", children: "+ " }),
|
|
774
|
-
/* @__PURE__ */ jsx3(Text3, { children: "Add account" })
|
|
775
|
-
] })
|
|
828
|
+
/* @__PURE__ */ jsx4(Text3, { children: col("", W.models, "left") }),
|
|
829
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.cost, children: col(tokens(totals.input), W.input) }),
|
|
830
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.cost, children: col(tokens(totals.output), W.output) }),
|
|
831
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.cost, children: col(tokens(totals.cacheCreate), W.cc) }),
|
|
832
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.cost, children: col(tokens(totals.cacheRead), W.cr) }),
|
|
833
|
+
W.total > 0 && /* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.cost, children: col(tokens(totals.input + totals.output + totals.cacheCreate + totals.cacheRead), W.total) }),
|
|
834
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.cost, children: col(currency(totals.cost), W.cost) })
|
|
776
835
|
] }),
|
|
777
|
-
/* @__PURE__ */
|
|
778
|
-
|
|
779
|
-
glyphs().arrowL,
|
|
780
|
-
glyphs().arrowR,
|
|
781
|
-
"/tab switch section ",
|
|
782
|
-
glyphs().middot,
|
|
783
|
-
" ",
|
|
784
|
-
glyphs().arrowD,
|
|
785
|
-
" rows ",
|
|
786
|
-
glyphs().middot,
|
|
787
|
-
" s/Esc close"
|
|
788
|
-
] }) : editingTz ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
789
|
-
"type IANA name (e.g. Europe/London) ",
|
|
790
|
-
glyphs().middot,
|
|
791
|
-
" empty = System ",
|
|
792
|
-
glyphs().middot,
|
|
793
|
-
" Enter save ",
|
|
794
|
-
glyphs().middot,
|
|
795
|
-
" Esc cancel"
|
|
796
|
-
] }) : editingAllowedHosts ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
797
|
-
"comma-separated exact DNS names ",
|
|
798
|
-
glyphs().middot,
|
|
799
|
-
" Enter save ",
|
|
800
|
-
glyphs().middot,
|
|
801
|
-
" Esc cancel"
|
|
802
|
-
] }) : activeTab === "providers" ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
836
|
+
/* @__PURE__ */ jsx4(Box3, { height: 1 }),
|
|
837
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
803
838
|
glyphs().arrowU,
|
|
804
839
|
glyphs().arrowD,
|
|
805
|
-
"
|
|
806
|
-
glyphs().middot,
|
|
807
|
-
" space toggle provider ",
|
|
808
|
-
glyphs().middot,
|
|
809
|
-
" s/Esc close"
|
|
810
|
-
] }) : activeTab === "accounts" && cursor >= 0 && cursor < trackedAccounts.length ? trackedAccounts[cursor]?.source === "auto" ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
811
|
-
glyphs().arrowU,
|
|
812
|
-
glyphs().arrowD,
|
|
813
|
-
" select ",
|
|
840
|
+
" navigate ",
|
|
814
841
|
glyphs().middot,
|
|
815
|
-
" Enter
|
|
842
|
+
" Enter detail ",
|
|
816
843
|
glyphs().middot,
|
|
817
|
-
"
|
|
844
|
+
" o sort ",
|
|
818
845
|
glyphs().middot,
|
|
819
|
-
"
|
|
820
|
-
] }) : /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
821
|
-
glyphs().arrowU,
|
|
822
|
-
glyphs().arrowD,
|
|
823
|
-
" select ",
|
|
846
|
+
" g/G top/bottom ",
|
|
824
847
|
glyphs().middot,
|
|
825
848
|
" ",
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
" reorder ",
|
|
830
|
-
glyphs().middot,
|
|
831
|
-
" Enter edit ",
|
|
832
|
-
glyphs().middot,
|
|
833
|
-
" space activate ",
|
|
834
|
-
glyphs().middot,
|
|
835
|
-
" d delete ",
|
|
836
|
-
glyphs().middot,
|
|
837
|
-
" s/Esc close"
|
|
838
|
-
] }) : activeTab === "accounts" && cursor === trackedAccounts.length ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
839
|
-
glyphs().arrowU,
|
|
840
|
-
glyphs().arrowD,
|
|
841
|
-
" select ",
|
|
842
|
-
glyphs().middot,
|
|
843
|
-
" Enter add account ",
|
|
844
|
-
glyphs().middot,
|
|
845
|
-
" s/Esc close"
|
|
846
|
-
] }) : /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
847
|
-
glyphs().arrowU,
|
|
848
|
-
glyphs().arrowD,
|
|
849
|
-
" select ",
|
|
850
|
-
glyphs().arrowL,
|
|
851
|
-
glyphs().arrowR,
|
|
852
|
-
" adjust Enter edit tab switch section s/Esc close"
|
|
849
|
+
clampedCursor + 1,
|
|
850
|
+
"/",
|
|
851
|
+
rows.length
|
|
853
852
|
] })
|
|
854
853
|
] });
|
|
855
854
|
});
|
|
856
|
-
function
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
855
|
+
function RowDetail({ row, indent }) {
|
|
856
|
+
const theme = useTuiTheme();
|
|
857
|
+
return /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", paddingLeft: indent, children: row.breakdown.map((m, i) => /* @__PURE__ */ jsxs3(Text3, { children: [
|
|
858
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
859
|
+
i === row.breakdown.length - 1 ? glyphs().treeEnd : glyphs().treeMid,
|
|
860
860
|
" "
|
|
861
861
|
] }),
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
862
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, children: col(m.name, 16, "left") }),
|
|
863
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
864
|
+
col(tokens(m.input), 8),
|
|
865
|
+
" in "
|
|
866
|
+
] }),
|
|
867
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
868
|
+
col(tokens(m.output), 8),
|
|
869
|
+
" out "
|
|
870
|
+
] }),
|
|
871
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
872
|
+
col(tokens(m.cacheCreate), 8),
|
|
873
|
+
" cc "
|
|
874
|
+
] }),
|
|
875
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
876
|
+
col(tokens(m.cacheRead), 9),
|
|
877
|
+
" cr "
|
|
878
|
+
] }),
|
|
879
|
+
/* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.cost, children: currency(m.cost) })
|
|
880
|
+
] }, m.name)) });
|
|
881
|
+
}
|
|
882
|
+
function fmtLabel(label) {
|
|
883
|
+
if (label.length === 7 && label[4] === "-") {
|
|
884
|
+
return `${MONTHS[Number(label.slice(5, 7))]} '${label.slice(2, 4)}`;
|
|
885
|
+
}
|
|
886
|
+
return shortDate(label);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// src/ui/onboarding.tsx
|
|
890
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
891
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
892
|
+
function Onboarding({ items, cursor, onToggle, onConfirm, heading = "Welcome to tokmon", subheading = "Pick the tools you want to track. You can change this anytime in settings." }) {
|
|
893
|
+
const theme = useTuiTheme();
|
|
894
|
+
const anyEnabled = items.some((it) => it.enabled);
|
|
895
|
+
const startIdx = items.length;
|
|
896
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, children: [
|
|
897
|
+
/* @__PURE__ */ jsx5(Box4, { children: /* @__PURE__ */ jsx5(Text4, { bold: true, color: theme.accent, children: heading }) }),
|
|
898
|
+
/* @__PURE__ */ jsx5(Text4, { dimColor: true, children: subheading }),
|
|
899
|
+
/* @__PURE__ */ jsx5(Box4, { height: 1 }),
|
|
900
|
+
items.map((it, i) => {
|
|
901
|
+
const selected = cursor === i;
|
|
902
|
+
const box = it.enabled ? `[${glyphs().check}]` : "[ ]";
|
|
903
|
+
return /* @__PURE__ */ jsxs4(ClickableBox, { onClick: () => onToggle(i), children: [
|
|
904
|
+
/* @__PURE__ */ jsxs4(Text4, { color: selected ? theme.accent : void 0, children: [
|
|
905
|
+
selected ? glyphs().caretR : " ",
|
|
868
906
|
" "
|
|
869
|
-
] })
|
|
907
|
+
] }),
|
|
908
|
+
/* @__PURE__ */ jsx5(Text4, { bold: it.enabled, color: it.enabled ? it.color : void 0, dimColor: !it.enabled, children: box }),
|
|
909
|
+
/* @__PURE__ */ jsxs4(Text4, { color: it.color, children: [
|
|
870
910
|
" ",
|
|
871
|
-
|
|
911
|
+
glyphs().dot,
|
|
872
912
|
" "
|
|
873
913
|
] }),
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
914
|
+
/* @__PURE__ */ jsx5(Box4, { width: 13, flexShrink: 0, children: /* @__PURE__ */ jsx5(Text4, { bold: selected, dimColor: !it.detected && !it.enabled, wrap: "truncate", children: it.name }) }),
|
|
915
|
+
it.detected ? /* @__PURE__ */ jsx5(Text4, { color: theme.ok, dimColor: true, children: "installed" }) : it.enabled ? /* @__PURE__ */ jsx5(Text4, { color: theme.warn, dimColor: true, children: "manual" }) : /* @__PURE__ */ jsx5(Text4, { dimColor: true, children: "not found" })
|
|
916
|
+
] }, it.id);
|
|
917
|
+
}),
|
|
918
|
+
/* @__PURE__ */ jsx5(Box4, { height: 1 }),
|
|
919
|
+
/* @__PURE__ */ jsxs4(ClickableBox, { onClick: onConfirm, children: [
|
|
920
|
+
/* @__PURE__ */ jsxs4(Text4, { color: cursor === startIdx ? theme.accent : void 0, children: [
|
|
921
|
+
cursor === startIdx ? glyphs().caretR : " ",
|
|
922
|
+
" "
|
|
923
|
+
] }),
|
|
924
|
+
/* @__PURE__ */ jsx5(Text4, { bold: true, color: anyEnabled ? theme.accent : void 0, dimColor: !anyEnabled, children: anyEnabled ? `${glyphs().play} Start tokmon` : `${glyphs().play} Start (nothing selected)` })
|
|
925
|
+
] }),
|
|
926
|
+
/* @__PURE__ */ jsx5(Box4, { height: 1 }),
|
|
927
|
+
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
928
|
+
glyphs().arrowU,
|
|
929
|
+
glyphs().arrowD,
|
|
930
|
+
" move ",
|
|
931
|
+
glyphs().middot,
|
|
932
|
+
" space toggle ",
|
|
933
|
+
glyphs().middot,
|
|
934
|
+
" enter start ",
|
|
935
|
+
glyphs().middot,
|
|
936
|
+
" q quit"
|
|
937
|
+
] })
|
|
877
938
|
] });
|
|
878
939
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
] });
|
|
940
|
+
|
|
941
|
+
// src/ui/loading.tsx
|
|
942
|
+
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
943
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
944
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
945
|
+
function accountReady(s, providerId) {
|
|
946
|
+
if (!s) return false;
|
|
947
|
+
const p = PROVIDERS[providerId];
|
|
948
|
+
if (p.hasBilling && (s.billingState === "error" || s.billing?.error)) return true;
|
|
949
|
+
if (p.hasUsage && s.summaryState !== "ready" && s.summaryState !== "error") return false;
|
|
950
|
+
if (p.hasBilling && s.billingState !== "ready" && s.billingState !== "error") return false;
|
|
951
|
+
return true;
|
|
892
952
|
}
|
|
893
|
-
function
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
children
|
|
901
|
-
] });
|
|
953
|
+
function statsReadyInput(s) {
|
|
954
|
+
if (!s) return void 0;
|
|
955
|
+
return {
|
|
956
|
+
summaryState: s.dashboard ? "ready" : "pending",
|
|
957
|
+
billingState: s.billing?.error ? "error" : s.billing ? "ready" : "pending",
|
|
958
|
+
billing: s.billing
|
|
959
|
+
};
|
|
902
960
|
}
|
|
903
|
-
function
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
961
|
+
function groupTodayCost(items) {
|
|
962
|
+
return items.reduce((sum, s) => {
|
|
963
|
+
const d = s?.dashboard;
|
|
964
|
+
return sum + (d?.today.cost ?? 0);
|
|
965
|
+
}, 0);
|
|
966
|
+
}
|
|
967
|
+
function headlineFor(group, items, privacyMode = false) {
|
|
968
|
+
const meta = PROVIDERS[group.provider];
|
|
969
|
+
if (meta.hasUsage) return `${currency(groupTodayCost(items))} today`;
|
|
970
|
+
const billing = items.map((s) => s?.billing).find(Boolean);
|
|
971
|
+
if (!billing) return "no data";
|
|
972
|
+
if (billing.error) return privacyMode ? redactEmail(billing.error) : billing.error;
|
|
973
|
+
const m = billing.metrics[0];
|
|
974
|
+
if (m) return metricValueText(m);
|
|
975
|
+
return billing.plan ?? "no data";
|
|
976
|
+
}
|
|
977
|
+
var STAGGER_FRAMES = 2;
|
|
978
|
+
function LoadingView({ groups, stats, cols, rows, readyInput, privacyMode = false }) {
|
|
979
|
+
const theme = useTuiTheme();
|
|
980
|
+
const resolveReady = readyInput ?? ((id) => statsReadyInput(stats.get(id)));
|
|
981
|
+
const sp = glyphs().spinner;
|
|
982
|
+
const [frame, setFrame] = useState2(0);
|
|
983
|
+
useEffect2(() => {
|
|
984
|
+
const id = setInterval(() => setFrame((f) => f + 1), 80);
|
|
985
|
+
return () => clearInterval(id);
|
|
986
|
+
}, []);
|
|
987
|
+
const nameW = Math.min(13, groups.reduce((w, g) => Math.max(w, PROVIDERS[g.provider].name.length), 0));
|
|
988
|
+
const readyCount = groups.filter((g) => g.accounts.every((a) => accountReady(resolveReady(a.id), g.provider))).length;
|
|
989
|
+
const maxRows = Math.max(1, rows - 7);
|
|
990
|
+
const visible = groups.slice(0, maxRows);
|
|
991
|
+
const hidden = groups.length - visible.length;
|
|
992
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
993
|
+
/* @__PURE__ */ jsxs5(Text5, { bold: true, color: theme.accent, children: [
|
|
994
|
+
glyphs().dotSel,
|
|
995
|
+
" tokmon"
|
|
996
|
+
] }),
|
|
997
|
+
/* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
998
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
999
|
+
"Detecting installed tools",
|
|
1000
|
+
glyphs().ellipsis
|
|
914
1001
|
] }),
|
|
915
|
-
/* @__PURE__ */
|
|
916
|
-
"
|
|
917
|
-
|
|
918
|
-
"
|
|
1002
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1003
|
+
" ",
|
|
1004
|
+
groups.length,
|
|
1005
|
+
" found"
|
|
919
1006
|
] })
|
|
920
1007
|
] }),
|
|
921
|
-
/* @__PURE__ */
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
label: "Home directory",
|
|
942
|
-
hint: `path containing the tool's data dir ${glyphs().middot} ~ for default`,
|
|
943
|
-
value: form.homeDir,
|
|
944
|
-
focused: form.field === "homeDir",
|
|
945
|
-
caret: form.caret,
|
|
946
|
-
accent,
|
|
947
|
-
placeholder: "~/work",
|
|
948
|
-
mono: true
|
|
1008
|
+
/* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
|
|
1009
|
+
visible.map((g, i) => {
|
|
1010
|
+
const meta = PROVIDERS[g.provider];
|
|
1011
|
+
const items = g.accounts.map((a) => stats.get(a.id));
|
|
1012
|
+
const ready = g.accounts.every((a) => accountReady(resolveReady(a.id), g.provider));
|
|
1013
|
+
const errored = items.some((s) => !!s?.billing?.error);
|
|
1014
|
+
const revealed = frame >= i * STAGGER_FRAMES;
|
|
1015
|
+
const name = truncateName(meta.name, nameW);
|
|
1016
|
+
const namePad = " ".repeat(Math.max(0, nameW - name.length));
|
|
1017
|
+
if (!revealed) {
|
|
1018
|
+
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1019
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1020
|
+
glyphs().dot,
|
|
1021
|
+
" "
|
|
1022
|
+
] }),
|
|
1023
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1024
|
+
name,
|
|
1025
|
+
namePad
|
|
1026
|
+
] })
|
|
1027
|
+
] }, g.provider);
|
|
949
1028
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1029
|
+
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1030
|
+
/* @__PURE__ */ jsxs5(Text5, { color: meta.color, children: [
|
|
1031
|
+
glyphs().dot,
|
|
1032
|
+
" "
|
|
1033
|
+
] }),
|
|
1034
|
+
/* @__PURE__ */ jsx6(Text5, { bold: true, color: meta.color, children: name }),
|
|
1035
|
+
/* @__PURE__ */ jsx6(Text5, { children: namePad }),
|
|
1036
|
+
/* @__PURE__ */ jsx6(Text5, { children: " " }),
|
|
1037
|
+
errored ? /* @__PURE__ */ jsxs5(Text5, { color: theme.crit, children: [
|
|
1038
|
+
glyphs().warn,
|
|
1039
|
+
" "
|
|
1040
|
+
] }) : ready ? /* @__PURE__ */ jsxs5(Text5, { color: theme.ok, children: [
|
|
1041
|
+
glyphs().check,
|
|
1042
|
+
" "
|
|
1043
|
+
] }) : /* @__PURE__ */ jsxs5(Text5, { color: theme.accent, children: [
|
|
1044
|
+
sp[frame % sp.length],
|
|
1045
|
+
" "
|
|
1046
|
+
] }),
|
|
1047
|
+
errored ? /* @__PURE__ */ jsx6(Text5, { color: theme.crit, children: headlineFor(g, items, privacyMode) }) : ready ? /* @__PURE__ */ jsx6(Text5, { children: headlineFor(g, items, privacyMode) }) : /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1048
|
+
"loading",
|
|
1049
|
+
glyphs().ellipsis
|
|
1050
|
+
] })
|
|
1051
|
+
] }, g.provider);
|
|
1052
|
+
}),
|
|
1053
|
+
hidden > 0 && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1054
|
+
"+",
|
|
1055
|
+
hidden,
|
|
1056
|
+
" more"
|
|
966
1057
|
] })
|
|
967
1058
|
] }),
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
975
|
-
"tab/",
|
|
976
|
-
glyphs().arrowU,
|
|
977
|
-
glyphs().arrowD,
|
|
978
|
-
" "
|
|
979
|
-
] }),
|
|
980
|
-
/* @__PURE__ */ jsx3(Text3, { children: "switch field" }),
|
|
981
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
982
|
-
" ",
|
|
983
|
-
glyphs().middot,
|
|
984
|
-
" "
|
|
985
|
-
] }),
|
|
986
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "enter " }),
|
|
987
|
-
/* @__PURE__ */ jsx3(Text3, { children: form.field === "color" ? "save" : "next" }),
|
|
988
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
989
|
-
" ",
|
|
990
|
-
glyphs().middot,
|
|
991
|
-
" "
|
|
992
|
-
] }),
|
|
993
|
-
form.field === "color" || form.field === "provider" ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
994
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
995
|
-
glyphs().arrowL,
|
|
996
|
-
glyphs().arrowR,
|
|
997
|
-
" "
|
|
998
|
-
] }),
|
|
999
|
-
/* @__PURE__ */ jsx3(Text3, { children: form.field === "provider" ? "pick provider" : "pick color" }),
|
|
1000
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1001
|
-
" ",
|
|
1002
|
-
glyphs().middot,
|
|
1003
|
-
" "
|
|
1004
|
-
] })
|
|
1005
|
-
] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1006
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1007
|
-
glyphs().arrowL,
|
|
1008
|
-
glyphs().arrowR,
|
|
1009
|
-
" "
|
|
1010
|
-
] }),
|
|
1011
|
-
/* @__PURE__ */ jsx3(Text3, { children: "move caret" }),
|
|
1012
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1013
|
-
" ",
|
|
1014
|
-
glyphs().middot,
|
|
1015
|
-
" "
|
|
1016
|
-
] })
|
|
1059
|
+
/* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
1060
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1061
|
+
"loading ",
|
|
1062
|
+
readyCount,
|
|
1063
|
+
" / ",
|
|
1064
|
+
groups.length
|
|
1017
1065
|
] }),
|
|
1018
|
-
/* @__PURE__ */
|
|
1019
|
-
/* @__PURE__ */ jsx3(Text3, { children: "save" }),
|
|
1020
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1066
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1021
1067
|
" ",
|
|
1022
1068
|
glyphs().middot,
|
|
1023
|
-
" "
|
|
1024
|
-
] })
|
|
1025
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "esc " }),
|
|
1026
|
-
/* @__PURE__ */ jsx3(Text3, { children: "cancel" })
|
|
1069
|
+
" W opens the web dashboard once loaded"
|
|
1070
|
+
] })
|
|
1027
1071
|
] })
|
|
1028
1072
|
] });
|
|
1029
1073
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
" "
|
|
1052
|
-
] })
|
|
1053
|
-
] }, s.id);
|
|
1054
|
-
}) });
|
|
1055
|
-
}
|
|
1056
|
-
function ProviderField({ value, focused }) {
|
|
1057
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
1058
|
-
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: focused ? PROVIDERS[value].color : void 0, bold: focused, dimColor: !focused, children: [
|
|
1059
|
-
focused ? glyphs().caretR : " ",
|
|
1060
|
-
" Provider"
|
|
1061
|
-
] }) }),
|
|
1062
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
1063
|
-
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
1064
|
-
" ",
|
|
1065
|
-
focused ? glyphs().vbar : " ",
|
|
1066
|
-
" "
|
|
1067
|
-
] }),
|
|
1068
|
-
PROVIDER_ORDER.map((pid) => {
|
|
1069
|
-
const selected = pid === value;
|
|
1070
|
-
const p = PROVIDERS[pid];
|
|
1071
|
-
return /* @__PURE__ */ jsx3(Box3, { marginRight: 2, children: selected ? /* @__PURE__ */ jsxs3(Text3, { bold: true, color: p.color, children: [
|
|
1072
|
-
"[",
|
|
1073
|
-
p.name,
|
|
1074
|
-
"]"
|
|
1075
|
-
] }) : /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: p.name }) }, pid);
|
|
1076
|
-
})
|
|
1077
|
-
] }),
|
|
1078
|
-
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " which tool this account tracks" }) })
|
|
1079
|
-
] });
|
|
1080
|
-
}
|
|
1081
|
-
function FormField({ label, hint, value, focused, caret, accent, placeholder, mono }) {
|
|
1082
|
-
const isPlaceholder = value === "";
|
|
1083
|
-
const display = isPlaceholder ? placeholder : value;
|
|
1084
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
1085
|
-
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: focused ? accent : void 0, bold: focused, dimColor: !focused, children: [
|
|
1086
|
-
focused ? glyphs().caretR : " ",
|
|
1087
|
-
" ",
|
|
1088
|
-
label
|
|
1089
|
-
] }) }),
|
|
1090
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
1091
|
-
/* @__PURE__ */ jsxs3(Text3, { color: focused ? accent : void 0, children: [
|
|
1092
|
-
" ",
|
|
1093
|
-
focused ? glyphs().vbar : " ",
|
|
1094
|
-
" "
|
|
1095
|
-
] }),
|
|
1096
|
-
focused ? isPlaceholder ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1097
|
-
/* @__PURE__ */ jsx3(Text3, { color: accent, children: glyphs().vbar }),
|
|
1098
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, italic: mono, children: placeholder })
|
|
1099
|
-
] }) : /* @__PURE__ */ jsx3(CaretText, { value, caret: caret ?? value.length, color: accent }) : /* @__PURE__ */ jsx3(Text3, { dimColor: isPlaceholder, italic: mono && isPlaceholder, children: display })
|
|
1100
|
-
] }),
|
|
1101
|
-
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1102
|
-
" ",
|
|
1103
|
-
hint
|
|
1104
|
-
] }) })
|
|
1105
|
-
] });
|
|
1074
|
+
|
|
1075
|
+
// src/ui/settings.tsx
|
|
1076
|
+
import { Fragment as Fragment4, memo as memo4 } from "react";
|
|
1077
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
1078
|
+
import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1079
|
+
var caret = (value) => /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1080
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1081
|
+
glyphs().caretL,
|
|
1082
|
+
" "
|
|
1083
|
+
] }),
|
|
1084
|
+
value,
|
|
1085
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1086
|
+
" ",
|
|
1087
|
+
glyphs().caretR
|
|
1088
|
+
] })
|
|
1089
|
+
] });
|
|
1090
|
+
var toggleText = (on, theme, onLabel, offLabel, offColor) => /* @__PURE__ */ jsx7(Text6, { bold: true, color: on ? theme.ok : offColor ?? theme.crit, children: on ? onLabel : offLabel });
|
|
1091
|
+
function cycleNumber(values, current, direction) {
|
|
1092
|
+
const exact = values.indexOf(current);
|
|
1093
|
+
const index = exact >= 0 ? exact : values.reduce((best, value, candidate) => Math.abs(value - current) < Math.abs(values[best] - current) ? candidate : best, 0);
|
|
1094
|
+
return values[(index + direction + values.length) % values.length];
|
|
1106
1095
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1096
|
+
var step = (key) => key.leftArrow ? -1 : 1;
|
|
1097
|
+
var isToggleKey = (key) => key.leftArrow || key.rightArrow || key.return;
|
|
1098
|
+
var isAdjustKey = (input, key) => input === " " || key.leftArrow || key.rightArrow || key.return;
|
|
1099
|
+
var GENERAL_SETTINGS = [
|
|
1100
|
+
{
|
|
1101
|
+
key: "refreshInterval",
|
|
1102
|
+
label: "Refresh interval",
|
|
1103
|
+
render: (rc) => caret(/* @__PURE__ */ jsxs6(Text6, { bold: true, color: rc.theme.cost, children: [
|
|
1104
|
+
rc.config.interval,
|
|
1105
|
+
"s"
|
|
1106
|
+
] })),
|
|
1107
|
+
onAdjust: (_input, key, ctx) => {
|
|
1108
|
+
if (key.leftArrow) ctx.global.updateConfig((c) => ({ ...c, interval: Math.max(1, c.interval - 1) }));
|
|
1109
|
+
if (key.rightArrow) ctx.global.updateConfig((c) => ({ ...c, interval: c.interval + 1 }));
|
|
1110
|
+
}
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
key: "billingPoll",
|
|
1114
|
+
label: "Billing poll",
|
|
1115
|
+
render: (rc) => caret(/* @__PURE__ */ jsxs6(Text6, { bold: true, color: rc.theme.cost, children: [
|
|
1116
|
+
rc.config.billingInterval,
|
|
1117
|
+
"m"
|
|
1118
|
+
] })),
|
|
1119
|
+
onAdjust: (_input, key, ctx) => {
|
|
1120
|
+
if (key.leftArrow) ctx.global.updateConfig((c) => ({ ...c, billingInterval: Math.max(1, c.billingInterval - 1) }));
|
|
1121
|
+
if (key.rightArrow) ctx.global.updateConfig((c) => ({ ...c, billingInterval: c.billingInterval + 1 }));
|
|
1122
|
+
}
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
key: "clearScreen",
|
|
1126
|
+
label: "Clear screen",
|
|
1127
|
+
render: (rc) => toggleText(rc.config.clearScreen, rc.theme, "on", "off"),
|
|
1128
|
+
onAdjust: (_input, key, ctx) => {
|
|
1129
|
+
if (isToggleKey(key)) ctx.global.updateConfig((c) => ({ ...c, clearScreen: !c.clearScreen }));
|
|
1130
|
+
}
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
key: "privacyMode",
|
|
1134
|
+
label: "Privacy mode",
|
|
1135
|
+
render: (rc) => toggleText(rc.config.privacyMode, rc.theme, "on", "off"),
|
|
1136
|
+
onAdjust: (_input, key, ctx) => {
|
|
1137
|
+
if (isToggleKey(key)) ctx.global.updateConfig((c) => ({ ...c, privacyMode: !c.privacyMode }));
|
|
1138
|
+
}
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
key: "privacyKey",
|
|
1142
|
+
label: "Privacy key",
|
|
1143
|
+
render: (rc) => /* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.cost, children: rc.config.privacyToggleKey === " " ? "space" : rc.config.privacyToggleKey }),
|
|
1144
|
+
onAdjust: (input, key, ctx) => {
|
|
1145
|
+
if (ctx.textInput.isPrintable(input, key)) {
|
|
1146
|
+
const clean = sanitizeTyped(input);
|
|
1147
|
+
if (clean.length === 1) ctx.global.updateConfig((c) => ({ ...c, privacyToggleKey: clean }));
|
|
1148
|
+
}
|
|
1149
|
+
if (key.backspace || key.delete) ctx.global.updateConfig((c) => ({ ...c, privacyToggleKey: "p" }));
|
|
1150
|
+
}
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
key: "timezone",
|
|
1154
|
+
label: "Timezone",
|
|
1155
|
+
render: (rc) => rc.tzEdit !== null ? /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1156
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "[" }),
|
|
1157
|
+
/* @__PURE__ */ jsx7(CaretText, { value: rc.tzEdit ?? "", caret: rc.tzCaret, color: rc.theme.accent }),
|
|
1158
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "]" })
|
|
1159
|
+
] }) : /* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.cost, children: rc.tzDisplay }),
|
|
1160
|
+
below: (rc, focused) => focused && rc.tzError ? /* @__PURE__ */ jsxs6(Text6, { color: rc.theme.crit, children: [
|
|
1161
|
+
" ",
|
|
1162
|
+
rc.tzError
|
|
1163
|
+
] }) : null,
|
|
1164
|
+
onAdjust: (_input, key, ctx) => {
|
|
1165
|
+
if (key.return) {
|
|
1166
|
+
const initial = ctx.global.config.timezone ?? "";
|
|
1167
|
+
ctx.timezoneEditor.setValue(initial);
|
|
1168
|
+
ctx.timezoneEditor.setCaret(initial.length);
|
|
1169
|
+
ctx.timezoneEditor.setError(null);
|
|
1170
|
+
}
|
|
1171
|
+
if (key.leftArrow || key.rightArrow) {
|
|
1172
|
+
ctx.global.updateConfig((c) => ({ ...c, timezone: c.timezone === null ? systemTimezone() : null }));
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
key: "dashboard",
|
|
1178
|
+
label: "Dashboard",
|
|
1179
|
+
render: (rc) => caret(/* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.cost, children: rc.config.dashboardLayout === "grid" ? "grid (all)" : "single (cycle)" })),
|
|
1180
|
+
onAdjust: (_input, key, ctx) => {
|
|
1181
|
+
if (isToggleKey(key)) ctx.global.updateConfig((c) => ({ ...c, dashboardLayout: c.dashboardLayout === "grid" ? "single" : "grid" }));
|
|
1182
|
+
}
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
key: "defaultFocus",
|
|
1186
|
+
label: "Default focus",
|
|
1187
|
+
render: (rc) => caret(/* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.cost, children: rc.config.defaultFocus === "all" ? "All" : "Last account" })),
|
|
1188
|
+
onAdjust: (_input, key, ctx) => {
|
|
1189
|
+
if (isToggleKey(key)) ctx.global.updateConfig((c) => ({ ...c, defaultFocus: c.defaultFocus === "all" ? "last" : "all" }));
|
|
1190
|
+
}
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
key: "networkAccess",
|
|
1194
|
+
label: "Network access",
|
|
1195
|
+
render: (rc) => /* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.config.allowNetworkAccess ? rc.theme.crit : rc.theme.ok, children: rc.config.allowNetworkAccess ? "LAN (unsafe)" : "local only" }),
|
|
1196
|
+
onAdjust: (_input, key, ctx) => {
|
|
1197
|
+
if (isToggleKey(key)) ctx.global.updateConfig((c) => ({ ...c, allowNetworkAccess: !c.allowNetworkAccess }));
|
|
1198
|
+
}
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
key: "allowedHosts",
|
|
1202
|
+
label: "Allowed hosts",
|
|
1203
|
+
render: (rc) => rc.allowedHostsEdit !== null ? /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1204
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "[" }),
|
|
1205
|
+
/* @__PURE__ */ jsx7(CaretText, { value: rc.allowedHostsEdit ?? "", caret: rc.allowedHostsCaret, color: rc.theme.accent }),
|
|
1206
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "]" })
|
|
1207
|
+
] }) : /* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.cost, children: rc.config.allowedHosts.join(", ") || "none" }),
|
|
1208
|
+
below: (rc, focused) => /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1209
|
+
focused && rc.allowedHostsError ? /* @__PURE__ */ jsxs6(Text6, { color: rc.theme.crit, children: [
|
|
1115
1210
|
" ",
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
COLOR_PALETTE.map((c) => /* @__PURE__ */ jsx3(Box3, { marginRight: 1, children: c === value ? /* @__PURE__ */ jsxs3(Text3, { bold: true, color: c, children: [
|
|
1120
|
-
"[",
|
|
1121
|
-
glyphs().dot,
|
|
1122
|
-
"]"
|
|
1123
|
-
] }) : /* @__PURE__ */ jsxs3(Text3, { color: c, dimColor: !focused, children: [
|
|
1124
|
-
" ",
|
|
1125
|
-
glyphs().dot
|
|
1126
|
-
] }) }, c))
|
|
1211
|
+
rc.allowedHostsError
|
|
1212
|
+
] }) : null,
|
|
1213
|
+
rc.config.allowNetworkAccess ? /* @__PURE__ */ jsx7(Text6, { color: rc.theme.crit, children: " Warning: dashboard data and settings will be exposed to your local network after daemon restart." }) : null
|
|
1127
1214
|
] }),
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
/* @__PURE__ */
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
/* @__PURE__ */
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1215
|
+
onAdjust: (_input, key, ctx) => {
|
|
1216
|
+
if (key.return) {
|
|
1217
|
+
const initial = ctx.global.config.allowedHosts.join(", ");
|
|
1218
|
+
ctx.allowedHostsEditor.setValue(initial);
|
|
1219
|
+
ctx.allowedHostsEditor.setCaret(initial.length);
|
|
1220
|
+
ctx.allowedHostsEditor.setError(null);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
},
|
|
1224
|
+
{
|
|
1225
|
+
key: "resetTimes",
|
|
1226
|
+
label: "Reset times",
|
|
1227
|
+
render: (rc) => caret(/* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.cost, children: rc.config.resetDisplay === "relative" ? "time remaining" : "exact date/time" })),
|
|
1228
|
+
onAdjust: (_input, key, ctx) => {
|
|
1229
|
+
if (isToggleKey(key)) ctx.global.updateConfig((c) => ({ ...c, resetDisplay: c.resetDisplay === "relative" ? "absolute" : "relative" }));
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
];
|
|
1233
|
+
var THEME_SETTINGS = [
|
|
1234
|
+
{
|
|
1235
|
+
key: "preset",
|
|
1236
|
+
label: "Preset",
|
|
1237
|
+
render: (rc) => caret(/* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.accent, children: themePresetLabel(rc.config.appearance.preset) })),
|
|
1238
|
+
onAdjust: (_input, key, ctx) => {
|
|
1239
|
+
if (!isToggleKey(key)) return;
|
|
1240
|
+
const direction = step(key);
|
|
1241
|
+
ctx.global.updateConfig((current) => {
|
|
1242
|
+
const choices = THEME_PRESET_IDS.filter((preset2) => preset2 !== "custom" || current.appearance.custom);
|
|
1243
|
+
const currentIndex = Math.max(0, choices.findIndex((v) => v === current.appearance.preset));
|
|
1244
|
+
const preset = choices[(currentIndex + direction + choices.length) % choices.length];
|
|
1245
|
+
return { ...current, appearance: { ...current.appearance, preset, ...isDarkOnlyThemePreset(preset) ? { mode: "dark" } : {} } };
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
},
|
|
1249
|
+
{
|
|
1250
|
+
key: "appAppearance",
|
|
1251
|
+
label: "App appearance",
|
|
1252
|
+
render: (rc) => caret(/* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.cost, children: appearanceModeLabel(rc.config.appearance.mode) })),
|
|
1253
|
+
onAdjust: (_input, key, ctx) => {
|
|
1254
|
+
if (!isToggleKey(key)) return;
|
|
1255
|
+
const direction = step(key);
|
|
1256
|
+
ctx.global.updateConfig((current) => {
|
|
1257
|
+
if (isDarkOnlyThemePreset(current.appearance.preset)) return current;
|
|
1258
|
+
const choices = ["auto", "light", "dark"];
|
|
1259
|
+
const currentIndex = choices.indexOf(current.appearance.mode);
|
|
1260
|
+
return { ...current, appearance: { ...current.appearance, mode: choices[(currentIndex + direction + choices.length) % choices.length] } };
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
{
|
|
1265
|
+
key: "terminalColors",
|
|
1266
|
+
label: "Terminal colors",
|
|
1267
|
+
render: (rc) => caret(/* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.accent, children: terminalModeLabel(rc.config.appearance.terminal) })),
|
|
1268
|
+
onAdjust: (_input, key, ctx) => {
|
|
1269
|
+
if (!isToggleKey(key)) return;
|
|
1270
|
+
const direction = step(key);
|
|
1271
|
+
ctx.global.updateConfig((current) => {
|
|
1272
|
+
const choices = ["ansi", "dark", "light", "off"];
|
|
1273
|
+
const currentIndex = choices.indexOf(current.appearance.terminal);
|
|
1274
|
+
return { ...current, appearance: { ...current.appearance, terminal: choices[(currentIndex + direction + choices.length) % choices.length] } };
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
];
|
|
1279
|
+
var DESKTOP_FIXED_SETTINGS = [
|
|
1280
|
+
{
|
|
1281
|
+
key: "trayApp",
|
|
1282
|
+
label: "Tray app",
|
|
1283
|
+
render: (rc) => toggleText(rc.config.tray.enabled, rc.theme, "on", "off"),
|
|
1284
|
+
onAdjust: (input, key, ctx) => {
|
|
1285
|
+
if (isAdjustKey(input, key)) ctx.global.updateConfig((c) => ({ ...c, tray: { ...c.tray, enabled: !c.tray.enabled } }));
|
|
1286
|
+
}
|
|
1287
|
+
},
|
|
1288
|
+
{
|
|
1289
|
+
key: "menuBarValues",
|
|
1290
|
+
label: "Menu bar values",
|
|
1291
|
+
render: (rc) => toggleText(rc.config.tray.showMenuBarText, rc.theme, "shown", "icon only", rc.theme.unknown),
|
|
1292
|
+
onAdjust: (input, key, ctx) => {
|
|
1293
|
+
if (isAdjustKey(input, key)) ctx.global.updateConfig((c) => ({ ...c, tray: { ...c.tray, showMenuBarText: !c.tray.showMenuBarText } }));
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
key: "summary",
|
|
1298
|
+
label: "Summary",
|
|
1299
|
+
render: (rc) => caret(/* @__PURE__ */ jsx7(Text6, { bold: true, color: rc.theme.accent, children: rc.config.tray.displayMetric === "smartHeadroom" ? "smart usage" : "tightest quota" })),
|
|
1300
|
+
onAdjust: (input, key, ctx) => {
|
|
1301
|
+
if (isAdjustKey(input, key)) ctx.global.updateConfig((c) => ({ ...c, tray: { ...c.tray, displayMetric: c.tray.displayMetric === "smartHeadroom" ? "tightestRemaining" : "smartHeadroom" } }));
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
{
|
|
1305
|
+
key: "trayRefresh",
|
|
1306
|
+
label: "Tray refresh",
|
|
1307
|
+
render: (rc) => caret(/* @__PURE__ */ jsxs6(Text6, { bold: true, color: rc.theme.cost, children: [
|
|
1308
|
+
rc.config.tray.pollIntervalSec,
|
|
1309
|
+
"s"
|
|
1310
|
+
] })),
|
|
1311
|
+
onAdjust: (input, key, ctx) => {
|
|
1312
|
+
if (isAdjustKey(input, key)) ctx.global.updateConfig((c) => ({ ...c, tray: { ...c.tray, pollIntervalSec: cycleNumber([15, 30, 60, 120], c.tray.pollIntervalSec, step(key)) } }));
|
|
1313
|
+
}
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
key: "activeWindow",
|
|
1317
|
+
label: "Active window",
|
|
1318
|
+
render: (rc) => caret(/* @__PURE__ */ jsxs6(Text6, { bold: true, color: rc.theme.cost, children: [
|
|
1319
|
+
rc.config.tray.activeTimeoutMin,
|
|
1320
|
+
"m"
|
|
1321
|
+
] })),
|
|
1322
|
+
onAdjust: (input, key, ctx) => {
|
|
1323
|
+
if (isAdjustKey(input, key)) ctx.global.updateConfig((c) => ({ ...c, tray: { ...c.tray, activeTimeoutMin: cycleNumber([5, 10, 15, 30], c.tray.activeTimeoutMin, step(key)) } }));
|
|
1324
|
+
}
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
key: "graphRange",
|
|
1328
|
+
label: "Graph range",
|
|
1329
|
+
render: (rc) => caret(/* @__PURE__ */ jsxs6(Text6, { bold: true, color: rc.theme.cost, children: [
|
|
1330
|
+
rc.config.desktop.graphRangeDays,
|
|
1331
|
+
" days"
|
|
1332
|
+
] })),
|
|
1333
|
+
onAdjust: (input, key, ctx) => {
|
|
1334
|
+
if (!isAdjustKey(input, key)) return;
|
|
1335
|
+
ctx.global.updateConfig((c) => ({
|
|
1336
|
+
...c,
|
|
1337
|
+
desktop: { ...c.desktop, graphRangeDays: cycleNumber(DESKTOP_GRAPH_RANGES, c.desktop.graphRangeDays, step(key)) }
|
|
1338
|
+
}));
|
|
1339
|
+
}
|
|
1340
|
+
},
|
|
1341
|
+
{
|
|
1342
|
+
key: "launchAtLogin",
|
|
1343
|
+
label: "Launch at login",
|
|
1344
|
+
render: (rc) => toggleText(rc.config.tray.launchAtLogin, rc.theme, "on", "off", rc.theme.unknown),
|
|
1345
|
+
onAdjust: (input, key, ctx) => {
|
|
1346
|
+
if (isAdjustKey(input, key)) ctx.global.updateConfig((c) => ({ ...c, tray: { ...c.tray, launchAtLogin: !c.tray.launchAtLogin } }));
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
];
|
|
1350
|
+
var GENERAL_ROWS = GENERAL_SETTINGS.length;
|
|
1351
|
+
var THEME_ROWS = THEME_SETTINGS.length;
|
|
1352
|
+
var DESKTOP_FIXED_ROWS = DESKTOP_FIXED_SETTINGS.length;
|
|
1353
|
+
var SETTINGS_TABS = ["general", "theme", "desktop", "providers", "accounts"];
|
|
1354
|
+
var SETTINGS_TAB_LABELS = {
|
|
1355
|
+
general: "General",
|
|
1356
|
+
theme: "Theme",
|
|
1357
|
+
desktop: "Desktop App",
|
|
1358
|
+
providers: "Providers",
|
|
1359
|
+
accounts: "Accounts"
|
|
1360
|
+
};
|
|
1361
|
+
var FORM_FIELDS = ["provider", "name", "homeDir", "color"];
|
|
1362
|
+
var SettingsView = memo4(function SettingsView2({
|
|
1363
|
+
config,
|
|
1364
|
+
cursor,
|
|
1365
|
+
activeTab,
|
|
1366
|
+
tzEdit,
|
|
1367
|
+
tzCaret,
|
|
1368
|
+
tzError,
|
|
1369
|
+
allowedHostsEdit,
|
|
1370
|
+
allowedHostsCaret,
|
|
1371
|
+
allowedHostsError,
|
|
1372
|
+
resolvedTz,
|
|
1373
|
+
accountForm,
|
|
1374
|
+
activeAccountId,
|
|
1375
|
+
trackedAccounts,
|
|
1376
|
+
accountIdentities
|
|
1377
|
+
}) {
|
|
1378
|
+
const theme = useTuiTheme();
|
|
1379
|
+
if (accountForm) return /* @__PURE__ */ jsx7(AccountFormView, { form: accountForm, accounts: config.accounts });
|
|
1380
|
+
const editingTz = tzEdit !== null;
|
|
1381
|
+
const editingAllowedHosts = allowedHostsEdit !== null;
|
|
1382
|
+
const tzDisplay = config.timezone === null ? `System (${resolvedTz})` : config.timezone;
|
|
1383
|
+
const tabFocused = cursor < 0;
|
|
1384
|
+
const rc = {
|
|
1385
|
+
config,
|
|
1386
|
+
theme,
|
|
1387
|
+
tzEdit,
|
|
1388
|
+
tzCaret,
|
|
1389
|
+
tzError,
|
|
1390
|
+
tzDisplay,
|
|
1391
|
+
allowedHostsEdit,
|
|
1392
|
+
allowedHostsCaret,
|
|
1393
|
+
allowedHostsError
|
|
1394
|
+
};
|
|
1395
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
1396
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, children: "Settings" }),
|
|
1397
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: configLocation() }),
|
|
1398
|
+
/* @__PURE__ */ jsx7(Box6, { height: 1 }),
|
|
1399
|
+
/* @__PURE__ */ jsx7(SettingsTabBar, { active: activeTab, focused: tabFocused }),
|
|
1400
|
+
/* @__PURE__ */ jsx7(Box6, { height: 1 }),
|
|
1401
|
+
activeTab === "general" && /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1402
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, dimColor: true, children: "General" }),
|
|
1403
|
+
GENERAL_SETTINGS.map((row, i) => /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
1404
|
+
/* @__PURE__ */ jsx7(Row, { cursor, idx: i, label: row.label, children: row.render(rc) }),
|
|
1405
|
+
row.below?.(rc, cursor === i)
|
|
1406
|
+
] }, row.key))
|
|
1169
1407
|
] }),
|
|
1170
|
-
|
|
1171
|
-
/* @__PURE__ */
|
|
1172
|
-
/* @__PURE__ */
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1408
|
+
activeTab === "theme" && /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1409
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, dimColor: true, children: "Theme" }),
|
|
1410
|
+
THEME_SETTINGS.map((row, i) => /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
1411
|
+
/* @__PURE__ */ jsx7(Row, { cursor, idx: i, label: row.label, children: row.render(rc) }),
|
|
1412
|
+
row.below?.(rc, cursor === i)
|
|
1413
|
+
] }, row.key)),
|
|
1414
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
1415
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " Auto follows the operating system in the web and desktop apps." }),
|
|
1416
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " Terminal colors never repaint your terminal background." }),
|
|
1417
|
+
config.appearance.preset === "custom" && /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " Custom colors are edited in the web dashboard and shared here." })
|
|
1177
1418
|
] })
|
|
1178
1419
|
] }),
|
|
1179
|
-
|
|
1180
|
-
/* @__PURE__ */
|
|
1181
|
-
/* @__PURE__ */
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
/* @__PURE__ */
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
" esc clear)"
|
|
1189
|
-
] })
|
|
1190
|
-
] }) : /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "/ filter" })
|
|
1191
|
-
] });
|
|
1192
|
-
});
|
|
1193
|
-
var TokenTable = memo4(function TokenTable2({ rows, cursor, expanded, maxRows, cols, onRowClick }) {
|
|
1194
|
-
if (rows.length === 0) return /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No usage in this period (or filtered out)." });
|
|
1195
|
-
const wide = cols > 90;
|
|
1196
|
-
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 };
|
|
1197
|
-
const fixed = base.label + base.input + base.output + base.cc + base.cr + base.total + base.cost;
|
|
1198
|
-
const available = cols - fixed - 6;
|
|
1199
|
-
const W = { ...base, models: Math.max(wide ? 22 : 14, available) };
|
|
1200
|
-
const lineW = W.label + W.models + W.input + W.output + W.cc + W.cr + W.total + W.cost;
|
|
1201
|
-
const totals = { input: 0, output: 0, cacheCreate: 0, cacheRead: 0, cost: 0 };
|
|
1202
|
-
for (const r of rows) {
|
|
1203
|
-
totals.input += r.input;
|
|
1204
|
-
totals.output += r.output;
|
|
1205
|
-
totals.cacheCreate += r.cacheCreate;
|
|
1206
|
-
totals.cacheRead += r.cacheRead;
|
|
1207
|
-
totals.cost += r.cost;
|
|
1208
|
-
}
|
|
1209
|
-
const clampedCursor = Math.min(cursor, rows.length - 1);
|
|
1210
|
-
const scrollStart = Math.max(0, Math.min(clampedCursor - Math.floor(maxRows / 2), rows.length - maxRows));
|
|
1211
|
-
const visible = rows.slice(scrollStart, scrollStart + maxRows);
|
|
1212
|
-
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
1213
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1214
|
-
/* @__PURE__ */ jsxs4(Text4, { bold: true, children: [
|
|
1215
|
-
" ",
|
|
1216
|
-
col("Date", W.label, "left")
|
|
1420
|
+
activeTab === "desktop" && /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1421
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, dimColor: true, children: "Desktop App" }),
|
|
1422
|
+
DESKTOP_FIXED_SETTINGS.map((row, i) => /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
1423
|
+
/* @__PURE__ */ jsx7(Row, { cursor, idx: i, label: row.label, children: row.render(rc) }),
|
|
1424
|
+
row.below?.(rc, cursor === i)
|
|
1425
|
+
] }, row.key)),
|
|
1426
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, children: [
|
|
1427
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, dimColor: true, children: "Menu bar pins" }),
|
|
1428
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " up to 2 providers, in order" })
|
|
1217
1429
|
] }),
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1430
|
+
PROVIDER_ORDER.map((pid, i) => {
|
|
1431
|
+
const idx = DESKTOP_FIXED_ROWS + i;
|
|
1432
|
+
const selected = cursor === idx;
|
|
1433
|
+
const pinned = config.tray.pinnedProviders.includes(pid);
|
|
1434
|
+
const position = config.tray.pinnedProviders.indexOf(pid);
|
|
1435
|
+
const meta = PROVIDERS[pid];
|
|
1436
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1437
|
+
/* @__PURE__ */ jsxs6(Text6, { color: selected ? theme.accent : void 0, children: [
|
|
1438
|
+
selected ? glyphs().caretR : " ",
|
|
1439
|
+
" "
|
|
1440
|
+
] }),
|
|
1441
|
+
/* @__PURE__ */ jsxs6(Box6, { width: 20, children: [
|
|
1442
|
+
/* @__PURE__ */ jsxs6(Text6, { color: meta.color, children: [
|
|
1443
|
+
glyphs().dot,
|
|
1444
|
+
" "
|
|
1445
|
+
] }),
|
|
1446
|
+
/* @__PURE__ */ jsx7(Text6, { bold: pinned, children: meta.name })
|
|
1447
|
+
] }),
|
|
1448
|
+
/* @__PURE__ */ jsx7(Text6, { color: pinned ? theme.ok : void 0, dimColor: !pinned, children: pinned ? `[${position + 1}] pinned` : "[ ] not pinned" })
|
|
1449
|
+
] }, pid);
|
|
1450
|
+
})
|
|
1225
1451
|
] }),
|
|
1226
|
-
/* @__PURE__ */
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1452
|
+
activeTab === "providers" && /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1453
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, dimColor: true, children: "Providers" }),
|
|
1454
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1455
|
+
/* @__PURE__ */ jsxs6(Text6, { color: cursor === 0 ? theme.accent : void 0, children: [
|
|
1456
|
+
cursor === 0 ? glyphs().caretR : " ",
|
|
1457
|
+
" "
|
|
1458
|
+
] }),
|
|
1459
|
+
/* @__PURE__ */ jsx7(Box6, { width: 20, children: /* @__PURE__ */ jsx7(Text6, { bold: true, children: "Discover accounts" }) }),
|
|
1460
|
+
toggleText(config.accountDetection.enabled, theme, "on", "off")
|
|
1461
|
+
] }),
|
|
1462
|
+
PROVIDER_ORDER.map((pid, i) => {
|
|
1463
|
+
const selected = cursor === i + 1;
|
|
1464
|
+
const enabled = !config.disabledProviders.includes(pid);
|
|
1465
|
+
const discovery = providerDetectionEnabled(config.accountDetection, pid);
|
|
1466
|
+
const p = PROVIDERS[pid];
|
|
1467
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1468
|
+
/* @__PURE__ */ jsxs6(Text6, { color: selected ? theme.accent : void 0, children: [
|
|
1469
|
+
selected ? glyphs().caretR : " ",
|
|
1470
|
+
" "
|
|
1235
1471
|
] }),
|
|
1236
|
-
/* @__PURE__ */
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
}),
|
|
1247
|
-
/* @__PURE__ */
|
|
1248
|
-
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
" ",
|
|
1251
|
-
|
|
1472
|
+
/* @__PURE__ */ jsxs6(Text6, { color: p.color, children: [
|
|
1473
|
+
" ",
|
|
1474
|
+
glyphs().dot,
|
|
1475
|
+
" "
|
|
1476
|
+
] }),
|
|
1477
|
+
/* @__PURE__ */ jsx7(Box6, { width: 16, children: /* @__PURE__ */ jsx7(Text6, { bold: selected, children: p.name }) }),
|
|
1478
|
+
/* @__PURE__ */ jsx7(Box6, { width: 18, children: /* @__PURE__ */ jsx7(Text6, { color: enabled ? theme.ok : void 0, dimColor: !enabled, children: enabled ? `[${glyphs().check}] tracking` : "[ ] tracking" }) }),
|
|
1479
|
+
/* @__PURE__ */ jsx7(Text6, { color: discovery ? theme.ok : void 0, dimColor: !discovery, children: discovery ? `[${glyphs().check}] auto-detect` : "[ ] auto-detect" })
|
|
1480
|
+
] }, pid);
|
|
1481
|
+
})
|
|
1482
|
+
] }),
|
|
1483
|
+
activeTab === "accounts" && /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1484
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, dimColor: true, children: "Accounts" }),
|
|
1485
|
+
trackedAccounts.length === 0 && /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1486
|
+
" none tracked ",
|
|
1487
|
+
glyphs().emDash,
|
|
1488
|
+
" enable a provider or add an account"
|
|
1252
1489
|
] }),
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1490
|
+
trackedAccounts.map((acc, i) => {
|
|
1491
|
+
const selected = cursor === i;
|
|
1492
|
+
const isActive = acc.id === activeAccountId;
|
|
1493
|
+
const provider = PROVIDERS[acc.providerId];
|
|
1494
|
+
const identity = accountIdentities.get(acc.id);
|
|
1495
|
+
const rawIdentityLabel = identity?.email || identity?.displayName || acc.name;
|
|
1496
|
+
const identityLabel = config.privacyMode ? redactEmail(rawIdentityLabel) : rawIdentityLabel;
|
|
1497
|
+
const plan = identity?.plan ?? null;
|
|
1498
|
+
const ignored = acc.source === "ignored";
|
|
1499
|
+
const sourceLabel = acc.source === "auto" ? "auto tracking" : ignored ? "ignored" : "configured";
|
|
1500
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1501
|
+
/* @__PURE__ */ jsxs6(Text6, { color: selected ? theme.accent : void 0, children: [
|
|
1502
|
+
selected ? glyphs().caretR : " ",
|
|
1503
|
+
" "
|
|
1504
|
+
] }),
|
|
1505
|
+
/* @__PURE__ */ jsxs6(Text6, { color: ignored ? theme.unknown : acc.color || provider.color, children: [
|
|
1506
|
+
ignored ? glyphs().warn : isActive ? glyphs().dot : glyphs().radioOff,
|
|
1507
|
+
" "
|
|
1508
|
+
] }),
|
|
1509
|
+
/* @__PURE__ */ jsx7(Box6, { width: 28, children: /* @__PURE__ */ jsx7(Text6, { bold: !ignored, dimColor: ignored, children: truncateName(identityLabel, 27) }) }),
|
|
1510
|
+
/* @__PURE__ */ jsx7(Box6, { width: 9, children: /* @__PURE__ */ jsx7(Text6, { color: provider.color, children: provider.name }) }),
|
|
1511
|
+
/* @__PURE__ */ jsx7(Box6, { width: 18, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: plan ? truncateName(plan, 17) : "" }) }),
|
|
1512
|
+
/* @__PURE__ */ jsx7(Box6, { width: 14, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: sourceLabel }) }),
|
|
1513
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: config.privacyMode ? "[path hidden]" : truncateName(acc.homeDir, 24) })
|
|
1514
|
+
] }, `${acc.source}:${acc.id}`);
|
|
1515
|
+
}),
|
|
1516
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1517
|
+
/* @__PURE__ */ jsxs6(Text6, { color: cursor === trackedAccounts.length ? theme.accent : void 0, children: [
|
|
1518
|
+
cursor === trackedAccounts.length ? glyphs().caretR : " ",
|
|
1519
|
+
" "
|
|
1520
|
+
] }),
|
|
1521
|
+
/* @__PURE__ */ jsx7(Text6, { color: theme.accent, children: "+ " }),
|
|
1522
|
+
/* @__PURE__ */ jsx7(Text6, { children: "Add account" })
|
|
1523
|
+
] })
|
|
1260
1524
|
] }),
|
|
1261
|
-
/* @__PURE__ */
|
|
1262
|
-
/* @__PURE__ */
|
|
1525
|
+
/* @__PURE__ */ jsx7(Box6, { height: 1 }),
|
|
1526
|
+
tabFocused ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1527
|
+
glyphs().arrowL,
|
|
1528
|
+
glyphs().arrowR,
|
|
1529
|
+
"/tab switch section ",
|
|
1530
|
+
glyphs().middot,
|
|
1531
|
+
" ",
|
|
1532
|
+
glyphs().arrowD,
|
|
1533
|
+
" rows ",
|
|
1534
|
+
glyphs().middot,
|
|
1535
|
+
" s/Esc close"
|
|
1536
|
+
] }) : editingTz ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1537
|
+
"type IANA name (e.g. Europe/London) ",
|
|
1538
|
+
glyphs().middot,
|
|
1539
|
+
" empty = System ",
|
|
1540
|
+
glyphs().middot,
|
|
1541
|
+
" Enter save ",
|
|
1542
|
+
glyphs().middot,
|
|
1543
|
+
" Esc cancel"
|
|
1544
|
+
] }) : editingAllowedHosts ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1545
|
+
"comma-separated exact DNS names ",
|
|
1546
|
+
glyphs().middot,
|
|
1547
|
+
" Enter save ",
|
|
1548
|
+
glyphs().middot,
|
|
1549
|
+
" Esc cancel"
|
|
1550
|
+
] }) : activeTab === "theme" ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1263
1551
|
glyphs().arrowU,
|
|
1264
1552
|
glyphs().arrowD,
|
|
1265
|
-
"
|
|
1553
|
+
" select ",
|
|
1266
1554
|
glyphs().middot,
|
|
1267
|
-
"
|
|
1555
|
+
" ",
|
|
1556
|
+
glyphs().arrowL,
|
|
1557
|
+
glyphs().arrowR,
|
|
1558
|
+
" adjust ",
|
|
1268
1559
|
glyphs().middot,
|
|
1269
|
-
"
|
|
1560
|
+
" s/Esc close"
|
|
1561
|
+
] }) : activeTab === "desktop" ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1562
|
+
glyphs().arrowU,
|
|
1563
|
+
glyphs().arrowD,
|
|
1564
|
+
" select ",
|
|
1270
1565
|
glyphs().middot,
|
|
1271
|
-
"
|
|
1566
|
+
" ",
|
|
1567
|
+
glyphs().arrowL,
|
|
1568
|
+
glyphs().arrowR,
|
|
1569
|
+
"/space adjust ",
|
|
1570
|
+
glyphs().middot,
|
|
1571
|
+
" pins are capped at 2 ",
|
|
1572
|
+
glyphs().middot,
|
|
1573
|
+
" s/Esc close"
|
|
1574
|
+
] }) : activeTab === "providers" ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1575
|
+
glyphs().arrowU,
|
|
1576
|
+
glyphs().arrowD,
|
|
1577
|
+
" select ",
|
|
1578
|
+
glyphs().middot,
|
|
1579
|
+
" space tracking/global discovery ",
|
|
1580
|
+
glyphs().middot,
|
|
1581
|
+
" a per-provider auto-detect ",
|
|
1582
|
+
glyphs().middot,
|
|
1583
|
+
" s/Esc close"
|
|
1584
|
+
] }) : activeTab === "accounts" && cursor >= 0 && cursor < trackedAccounts.length ? trackedAccounts[cursor]?.source === "auto" ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1585
|
+
glyphs().arrowU,
|
|
1586
|
+
glyphs().arrowD,
|
|
1587
|
+
" select ",
|
|
1588
|
+
glyphs().middot,
|
|
1589
|
+
" Enter configure ",
|
|
1590
|
+
glyphs().middot,
|
|
1591
|
+
" space activate ",
|
|
1592
|
+
glyphs().middot,
|
|
1593
|
+
" x ignore ",
|
|
1594
|
+
glyphs().middot,
|
|
1595
|
+
" s/Esc close"
|
|
1596
|
+
] }) : trackedAccounts[cursor]?.source === "ignored" ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1597
|
+
glyphs().arrowU,
|
|
1598
|
+
glyphs().arrowD,
|
|
1599
|
+
" select ",
|
|
1600
|
+
glyphs().middot,
|
|
1601
|
+
" Enter/x turn on ",
|
|
1602
|
+
glyphs().middot,
|
|
1603
|
+
" s/Esc close"
|
|
1604
|
+
] }) : /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1605
|
+
glyphs().arrowU,
|
|
1606
|
+
glyphs().arrowD,
|
|
1607
|
+
" select ",
|
|
1272
1608
|
glyphs().middot,
|
|
1273
1609
|
" ",
|
|
1274
|
-
|
|
1275
|
-
"/",
|
|
1276
|
-
rows.length
|
|
1277
|
-
] })
|
|
1278
|
-
] });
|
|
1279
|
-
});
|
|
1280
|
-
function RowDetail({ row, indent }) {
|
|
1281
|
-
return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", paddingLeft: indent, children: row.breakdown.map((m, i) => /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1282
|
-
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
1283
|
-
i === row.breakdown.length - 1 ? glyphs().treeEnd : glyphs().treeMid,
|
|
1284
|
-
" "
|
|
1285
|
-
] }),
|
|
1286
|
-
/* @__PURE__ */ jsx4(Text4, { bold: true, children: col(m.name, 16, "left") }),
|
|
1287
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1288
|
-
col(tokens(m.input), 8),
|
|
1289
|
-
" in "
|
|
1290
|
-
] }),
|
|
1291
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1292
|
-
col(tokens(m.output), 8),
|
|
1293
|
-
" out "
|
|
1294
|
-
] }),
|
|
1295
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1296
|
-
col(tokens(m.cacheCreate), 8),
|
|
1297
|
-
" cc "
|
|
1298
|
-
] }),
|
|
1299
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1300
|
-
col(tokens(m.cacheRead), 9),
|
|
1301
|
-
" cr "
|
|
1302
|
-
] }),
|
|
1303
|
-
/* @__PURE__ */ jsx4(Text4, { bold: true, color: "yellow", children: currency(m.cost) })
|
|
1304
|
-
] }, m.name)) });
|
|
1305
|
-
}
|
|
1306
|
-
function fmtLabel(label) {
|
|
1307
|
-
if (label.length === 7 && label[4] === "-") {
|
|
1308
|
-
return `${MONTHS[Number(label.slice(5, 7))]} '${label.slice(2, 4)}`;
|
|
1309
|
-
}
|
|
1310
|
-
return shortDate(label);
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
// src/ui/onboarding.tsx
|
|
1314
|
-
import { Box as Box5, Text as Text5 } from "ink";
|
|
1315
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1316
|
-
function Onboarding({ items, cursor, onToggle, onConfirm, heading = "Welcome to tokmon", subheading = "Pick the tools you want to track. You can change this anytime in settings." }) {
|
|
1317
|
-
const anyEnabled = items.some((it) => it.enabled);
|
|
1318
|
-
const startIdx = items.length;
|
|
1319
|
-
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
1320
|
-
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { bold: true, color: "greenBright", children: heading }) }),
|
|
1321
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: subheading }),
|
|
1322
|
-
/* @__PURE__ */ jsx5(Box5, { height: 1 }),
|
|
1323
|
-
items.map((it, i) => {
|
|
1324
|
-
const selected = cursor === i;
|
|
1325
|
-
const box = it.enabled ? `[${glyphs().check}]` : "[ ]";
|
|
1326
|
-
return /* @__PURE__ */ jsxs5(ClickableBox, { onClick: () => onToggle(i), children: [
|
|
1327
|
-
/* @__PURE__ */ jsxs5(Text5, { color: selected ? "green" : void 0, children: [
|
|
1328
|
-
selected ? glyphs().caretR : " ",
|
|
1329
|
-
" "
|
|
1330
|
-
] }),
|
|
1331
|
-
/* @__PURE__ */ jsx5(Text5, { bold: it.enabled, color: it.enabled ? it.color : void 0, dimColor: !it.enabled, children: box }),
|
|
1332
|
-
/* @__PURE__ */ jsxs5(Text5, { color: it.color, children: [
|
|
1333
|
-
" ",
|
|
1334
|
-
glyphs().dot,
|
|
1335
|
-
" "
|
|
1336
|
-
] }),
|
|
1337
|
-
/* @__PURE__ */ jsx5(Box5, { width: 13, flexShrink: 0, children: /* @__PURE__ */ jsx5(Text5, { bold: selected, dimColor: !it.detected && !it.enabled, wrap: "truncate", children: it.name }) }),
|
|
1338
|
-
it.detected ? /* @__PURE__ */ jsx5(Text5, { color: "green", dimColor: true, children: "installed" }) : it.enabled ? /* @__PURE__ */ jsx5(Text5, { color: "yellow", dimColor: true, children: "manual" }) : /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "not found" })
|
|
1339
|
-
] }, it.id);
|
|
1340
|
-
}),
|
|
1341
|
-
/* @__PURE__ */ jsx5(Box5, { height: 1 }),
|
|
1342
|
-
/* @__PURE__ */ jsxs5(ClickableBox, { onClick: onConfirm, children: [
|
|
1343
|
-
/* @__PURE__ */ jsxs5(Text5, { color: cursor === startIdx ? "green" : void 0, children: [
|
|
1344
|
-
cursor === startIdx ? glyphs().caretR : " ",
|
|
1345
|
-
" "
|
|
1346
|
-
] }),
|
|
1347
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, color: anyEnabled ? "greenBright" : void 0, dimColor: !anyEnabled, children: anyEnabled ? `${glyphs().play} Start tokmon` : `${glyphs().play} Start (nothing selected)` })
|
|
1348
|
-
] }),
|
|
1349
|
-
/* @__PURE__ */ jsx5(Box5, { height: 1 }),
|
|
1350
|
-
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1610
|
+
glyphs().shift,
|
|
1351
1611
|
glyphs().arrowU,
|
|
1352
1612
|
glyphs().arrowD,
|
|
1353
|
-
"
|
|
1613
|
+
" reorder ",
|
|
1354
1614
|
glyphs().middot,
|
|
1355
|
-
"
|
|
1615
|
+
" Enter edit ",
|
|
1356
1616
|
glyphs().middot,
|
|
1357
|
-
"
|
|
1617
|
+
" space activate ",
|
|
1358
1618
|
glyphs().middot,
|
|
1359
|
-
"
|
|
1619
|
+
" d delete ",
|
|
1620
|
+
glyphs().middot,
|
|
1621
|
+
" s/Esc close"
|
|
1622
|
+
] }) : activeTab === "accounts" && cursor === trackedAccounts.length ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1623
|
+
glyphs().arrowU,
|
|
1624
|
+
glyphs().arrowD,
|
|
1625
|
+
" select ",
|
|
1626
|
+
glyphs().middot,
|
|
1627
|
+
" Enter add account ",
|
|
1628
|
+
glyphs().middot,
|
|
1629
|
+
" s/Esc close"
|
|
1630
|
+
] }) : /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1631
|
+
glyphs().arrowU,
|
|
1632
|
+
glyphs().arrowD,
|
|
1633
|
+
" select ",
|
|
1634
|
+
glyphs().arrowL,
|
|
1635
|
+
glyphs().arrowR,
|
|
1636
|
+
" adjust Enter edit tab switch section s/Esc close"
|
|
1360
1637
|
] })
|
|
1361
1638
|
] });
|
|
1639
|
+
});
|
|
1640
|
+
function themePresetLabel(preset) {
|
|
1641
|
+
return themePresetOption(preset).name;
|
|
1362
1642
|
}
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
1366
|
-
import { Box as Box6, Text as Text6 } from "ink";
|
|
1367
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1368
|
-
function accountReady(s, providerId) {
|
|
1369
|
-
if (!s) return false;
|
|
1370
|
-
const p = PROVIDERS[providerId];
|
|
1371
|
-
if (p.hasBilling && (s.billingState === "error" || s.billing?.error)) return true;
|
|
1372
|
-
if (p.hasUsage && s.summaryState !== "ready" && s.summaryState !== "error") return false;
|
|
1373
|
-
if (p.hasBilling && s.billingState !== "ready" && s.billingState !== "error") return false;
|
|
1374
|
-
return true;
|
|
1643
|
+
function appearanceModeLabel(mode) {
|
|
1644
|
+
return mode === "auto" ? "Auto (system)" : mode === "light" ? "Light" : "Dark";
|
|
1375
1645
|
}
|
|
1376
|
-
function
|
|
1377
|
-
|
|
1378
|
-
return {
|
|
1379
|
-
summaryState: s.dashboard ? "ready" : "pending",
|
|
1380
|
-
billingState: s.billing?.error ? "error" : s.billing ? "ready" : "pending",
|
|
1381
|
-
billing: s.billing
|
|
1382
|
-
};
|
|
1646
|
+
function terminalModeLabel(mode) {
|
|
1647
|
+
return mode === "ansi" ? "Terminal managed" : mode === "off" ? "No color" : mode === "light" ? "Light terminal" : "Dark terminal";
|
|
1383
1648
|
}
|
|
1384
|
-
function
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1649
|
+
function SettingsTabBar({ active, focused }) {
|
|
1650
|
+
const theme = useTuiTheme();
|
|
1651
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1652
|
+
/* @__PURE__ */ jsxs6(Text6, { color: focused ? theme.accent : void 0, children: [
|
|
1653
|
+
focused ? glyphs().caretR : " ",
|
|
1654
|
+
" "
|
|
1655
|
+
] }),
|
|
1656
|
+
SETTINGS_TABS.map((tab, i) => {
|
|
1657
|
+
const selected = tab === active;
|
|
1658
|
+
return /* @__PURE__ */ jsxs6(Box6, { marginRight: 1, children: [
|
|
1659
|
+
selected ? /* @__PURE__ */ jsxs6(Text6, { bold: true, inverse: true, children: [
|
|
1660
|
+
" ",
|
|
1661
|
+
SETTINGS_TAB_LABELS[tab],
|
|
1662
|
+
" "
|
|
1663
|
+
] }) : /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1664
|
+
" ",
|
|
1665
|
+
SETTINGS_TAB_LABELS[tab],
|
|
1666
|
+
" "
|
|
1667
|
+
] }),
|
|
1668
|
+
i < SETTINGS_TABS.length - 1 && /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " " })
|
|
1669
|
+
] }, tab);
|
|
1670
|
+
})
|
|
1671
|
+
] });
|
|
1399
1672
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
const id = setInterval(() => setFrame((f) => f + 1), 80);
|
|
1407
|
-
return () => clearInterval(id);
|
|
1408
|
-
}, []);
|
|
1409
|
-
const nameW = Math.min(13, groups.reduce((w, g) => Math.max(w, PROVIDERS[g.provider].name.length), 0));
|
|
1410
|
-
const readyCount = groups.filter((g) => g.accounts.every((a) => accountReady(resolveReady(a.id), g.provider))).length;
|
|
1411
|
-
const maxRows = Math.max(1, rows - 7);
|
|
1412
|
-
const visible = groups.slice(0, maxRows);
|
|
1413
|
-
const hidden = groups.length - visible.length;
|
|
1414
|
-
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1415
|
-
/* @__PURE__ */ jsxs6(Text6, { bold: true, color: "greenBright", children: [
|
|
1416
|
-
glyphs().dotSel,
|
|
1417
|
-
" tokmon"
|
|
1673
|
+
function Row({ cursor, idx, label, children }) {
|
|
1674
|
+
const theme = useTuiTheme();
|
|
1675
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1676
|
+
/* @__PURE__ */ jsxs6(Text6, { color: cursor === idx ? theme.accent : void 0, children: [
|
|
1677
|
+
cursor === idx ? glyphs().caretR : " ",
|
|
1678
|
+
" "
|
|
1418
1679
|
] }),
|
|
1419
|
-
/* @__PURE__ */
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1680
|
+
/* @__PURE__ */ jsx7(Box6, { width: 20, children: /* @__PURE__ */ jsx7(Text6, { children: label }) }),
|
|
1681
|
+
children
|
|
1682
|
+
] });
|
|
1683
|
+
}
|
|
1684
|
+
function AccountFormView({ form, accounts }) {
|
|
1685
|
+
const theme = useTuiTheme();
|
|
1686
|
+
const previewId = form.mode === "add" ? generateAccountId(form.name || "account", accounts) : form.editingId ?? "";
|
|
1687
|
+
const accent = form.color;
|
|
1688
|
+
const stepIndex = { provider: 1, name: 2, homeDir: 3, color: 4 };
|
|
1689
|
+
const step2 = stepIndex[form.field];
|
|
1690
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
1691
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1692
|
+
/* @__PURE__ */ jsx7(Text6, { color: accent, bold: true, children: glyphs().vbar }),
|
|
1693
|
+
/* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
|
|
1694
|
+
" ",
|
|
1695
|
+
form.mode === "add" ? "NEW ACCOUNT" : "EDIT ACCOUNT"
|
|
1423
1696
|
] }),
|
|
1424
1697
|
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1425
|
-
"
|
|
1426
|
-
|
|
1427
|
-
"
|
|
1698
|
+
" step ",
|
|
1699
|
+
step2,
|
|
1700
|
+
" of 4"
|
|
1428
1701
|
] })
|
|
1429
1702
|
] }),
|
|
1430
|
-
/* @__PURE__ */
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
] }),
|
|
1445
|
-
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1446
|
-
name,
|
|
1447
|
-
namePad
|
|
1448
|
-
] })
|
|
1449
|
-
] }, g.provider);
|
|
1703
|
+
/* @__PURE__ */ jsx7(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx7(Stepper, { active: form.field, accent }) }),
|
|
1704
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", borderStyle: glyphs().border, borderColor: accent, paddingX: 2, paddingY: 1, children: [
|
|
1705
|
+
/* @__PURE__ */ jsx7(ProviderField, { value: form.providerId, focused: form.field === "provider" }),
|
|
1706
|
+
/* @__PURE__ */ jsx7(Box6, { height: 1 }),
|
|
1707
|
+
/* @__PURE__ */ jsx7(
|
|
1708
|
+
FormField,
|
|
1709
|
+
{
|
|
1710
|
+
label: "Name",
|
|
1711
|
+
hint: "display name for this account",
|
|
1712
|
+
value: form.name,
|
|
1713
|
+
focused: form.field === "name",
|
|
1714
|
+
caret: form.caret,
|
|
1715
|
+
accent,
|
|
1716
|
+
placeholder: "e.g. Work, Personal"
|
|
1450
1717
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1718
|
+
),
|
|
1719
|
+
/* @__PURE__ */ jsx7(Box6, { height: 1 }),
|
|
1720
|
+
/* @__PURE__ */ jsx7(
|
|
1721
|
+
FormField,
|
|
1722
|
+
{
|
|
1723
|
+
label: "Home directory",
|
|
1724
|
+
hint: `path containing the tool's data dir ${glyphs().middot} ~ for default`,
|
|
1725
|
+
value: form.homeDir,
|
|
1726
|
+
focused: form.field === "homeDir",
|
|
1727
|
+
caret: form.caret,
|
|
1728
|
+
accent,
|
|
1729
|
+
placeholder: "~/work",
|
|
1730
|
+
mono: true
|
|
1731
|
+
}
|
|
1732
|
+
),
|
|
1733
|
+
/* @__PURE__ */ jsx7(Box6, { height: 1 }),
|
|
1734
|
+
/* @__PURE__ */ jsx7(ColorField, { value: form.color, focused: form.field === "color" }),
|
|
1735
|
+
/* @__PURE__ */ jsx7(Box6, { height: 1 }),
|
|
1736
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1737
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1738
|
+
"id ",
|
|
1739
|
+
glyphs().boxMark,
|
|
1740
|
+
" "
|
|
1741
|
+
] }),
|
|
1742
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: accent, children: previewId || "account" }),
|
|
1743
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1744
|
+
" ",
|
|
1745
|
+
glyphs().boxMark,
|
|
1746
|
+
" auto-generated from name"
|
|
1747
|
+
] })
|
|
1479
1748
|
] })
|
|
1480
1749
|
] }),
|
|
1750
|
+
form.error && /* @__PURE__ */ jsx7(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: theme.crit, children: [
|
|
1751
|
+
glyphs().warn,
|
|
1752
|
+
" ",
|
|
1753
|
+
form.error
|
|
1754
|
+
] }) }),
|
|
1481
1755
|
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, children: [
|
|
1482
1756
|
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1483
|
-
"
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1757
|
+
"tab/",
|
|
1758
|
+
glyphs().arrowU,
|
|
1759
|
+
glyphs().arrowD,
|
|
1760
|
+
" "
|
|
1487
1761
|
] }),
|
|
1762
|
+
/* @__PURE__ */ jsx7(Text6, { children: "switch field" }),
|
|
1488
1763
|
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1489
1764
|
" ",
|
|
1490
1765
|
glyphs().middot,
|
|
1491
|
-
"
|
|
1492
|
-
] })
|
|
1766
|
+
" "
|
|
1767
|
+
] }),
|
|
1768
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "enter " }),
|
|
1769
|
+
/* @__PURE__ */ jsx7(Text6, { children: form.field === "color" ? "save" : "next" }),
|
|
1770
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1771
|
+
" ",
|
|
1772
|
+
glyphs().middot,
|
|
1773
|
+
" "
|
|
1774
|
+
] }),
|
|
1775
|
+
form.field === "color" || form.field === "provider" ? /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1776
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1777
|
+
glyphs().arrowL,
|
|
1778
|
+
glyphs().arrowR,
|
|
1779
|
+
" "
|
|
1780
|
+
] }),
|
|
1781
|
+
/* @__PURE__ */ jsx7(Text6, { children: form.field === "provider" ? "pick provider" : "pick color" }),
|
|
1782
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1783
|
+
" ",
|
|
1784
|
+
glyphs().middot,
|
|
1785
|
+
" "
|
|
1786
|
+
] })
|
|
1787
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1788
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1789
|
+
glyphs().arrowL,
|
|
1790
|
+
glyphs().arrowR,
|
|
1791
|
+
" "
|
|
1792
|
+
] }),
|
|
1793
|
+
/* @__PURE__ */ jsx7(Text6, { children: "move caret" }),
|
|
1794
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1795
|
+
" ",
|
|
1796
|
+
glyphs().middot,
|
|
1797
|
+
" "
|
|
1798
|
+
] })
|
|
1799
|
+
] }),
|
|
1800
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "ctrl+s " }),
|
|
1801
|
+
/* @__PURE__ */ jsx7(Text6, { children: "save" }),
|
|
1802
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1803
|
+
" ",
|
|
1804
|
+
glyphs().middot,
|
|
1805
|
+
" "
|
|
1806
|
+
] }),
|
|
1807
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "esc " }),
|
|
1808
|
+
/* @__PURE__ */ jsx7(Text6, { children: "cancel" })
|
|
1493
1809
|
] })
|
|
1494
1810
|
] });
|
|
1495
1811
|
}
|
|
1812
|
+
function Stepper({ active, accent }) {
|
|
1813
|
+
const steps = [
|
|
1814
|
+
{ id: "provider", label: "Provider" },
|
|
1815
|
+
{ id: "name", label: "Name" },
|
|
1816
|
+
{ id: "homeDir", label: "Home" },
|
|
1817
|
+
{ id: "color", label: "Color" }
|
|
1818
|
+
];
|
|
1819
|
+
const activeIdx = steps.findIndex((s) => s.id === active);
|
|
1820
|
+
return /* @__PURE__ */ jsx7(Box6, { children: steps.map((s, i) => {
|
|
1821
|
+
const done = i < activeIdx;
|
|
1822
|
+
const cur = i === activeIdx;
|
|
1823
|
+
const dot = done ? glyphs().dot : cur ? glyphs().dotSel : glyphs().radioOff;
|
|
1824
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1825
|
+
/* @__PURE__ */ jsxs6(Text6, { color: cur || done ? accent : void 0, dimColor: !cur && !done, children: [
|
|
1826
|
+
dot,
|
|
1827
|
+
" "
|
|
1828
|
+
] }),
|
|
1829
|
+
/* @__PURE__ */ jsx7(Text6, { bold: cur, color: cur ? accent : void 0, dimColor: !cur, children: s.label }),
|
|
1830
|
+
i < steps.length - 1 && /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1831
|
+
" ",
|
|
1832
|
+
glyphs().rule,
|
|
1833
|
+
" "
|
|
1834
|
+
] })
|
|
1835
|
+
] }, s.id);
|
|
1836
|
+
}) });
|
|
1837
|
+
}
|
|
1838
|
+
function ProviderField({ value, focused }) {
|
|
1839
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1840
|
+
/* @__PURE__ */ jsx7(Box6, { children: /* @__PURE__ */ jsxs6(Text6, { color: focused ? PROVIDERS[value].color : void 0, bold: focused, dimColor: !focused, children: [
|
|
1841
|
+
focused ? glyphs().caretR : " ",
|
|
1842
|
+
" Provider"
|
|
1843
|
+
] }) }),
|
|
1844
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1845
|
+
/* @__PURE__ */ jsxs6(Text6, { children: [
|
|
1846
|
+
" ",
|
|
1847
|
+
focused ? glyphs().vbar : " ",
|
|
1848
|
+
" "
|
|
1849
|
+
] }),
|
|
1850
|
+
PROVIDER_ORDER.map((pid) => {
|
|
1851
|
+
const selected = pid === value;
|
|
1852
|
+
const p = PROVIDERS[pid];
|
|
1853
|
+
return /* @__PURE__ */ jsx7(Box6, { marginRight: 2, children: selected ? /* @__PURE__ */ jsxs6(Text6, { bold: true, color: p.color, children: [
|
|
1854
|
+
"[",
|
|
1855
|
+
p.name,
|
|
1856
|
+
"]"
|
|
1857
|
+
] }) : /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: p.name }) }, pid);
|
|
1858
|
+
})
|
|
1859
|
+
] }),
|
|
1860
|
+
/* @__PURE__ */ jsx7(Box6, { children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " which tool this account tracks" }) })
|
|
1861
|
+
] });
|
|
1862
|
+
}
|
|
1863
|
+
function FormField({ label, hint, value, focused, caret: caret2, accent, placeholder, mono }) {
|
|
1864
|
+
const isPlaceholder = value === "";
|
|
1865
|
+
const display = isPlaceholder ? placeholder : value;
|
|
1866
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1867
|
+
/* @__PURE__ */ jsx7(Box6, { children: /* @__PURE__ */ jsxs6(Text6, { color: focused ? accent : void 0, bold: focused, dimColor: !focused, children: [
|
|
1868
|
+
focused ? glyphs().caretR : " ",
|
|
1869
|
+
" ",
|
|
1870
|
+
label
|
|
1871
|
+
] }) }),
|
|
1872
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1873
|
+
/* @__PURE__ */ jsxs6(Text6, { color: focused ? accent : void 0, children: [
|
|
1874
|
+
" ",
|
|
1875
|
+
focused ? glyphs().vbar : " ",
|
|
1876
|
+
" "
|
|
1877
|
+
] }),
|
|
1878
|
+
focused ? isPlaceholder ? /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1879
|
+
/* @__PURE__ */ jsx7(Text6, { color: accent, children: glyphs().vbar }),
|
|
1880
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, italic: mono, children: placeholder })
|
|
1881
|
+
] }) : /* @__PURE__ */ jsx7(CaretText, { value, caret: caret2 ?? value.length, color: accent }) : /* @__PURE__ */ jsx7(Text6, { dimColor: isPlaceholder, italic: mono && isPlaceholder, children: display })
|
|
1882
|
+
] }),
|
|
1883
|
+
/* @__PURE__ */ jsx7(Box6, { children: /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1884
|
+
" ",
|
|
1885
|
+
hint
|
|
1886
|
+
] }) })
|
|
1887
|
+
] });
|
|
1888
|
+
}
|
|
1889
|
+
function ColorField({ value, focused }) {
|
|
1890
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1891
|
+
/* @__PURE__ */ jsx7(Box6, { children: /* @__PURE__ */ jsxs6(Text6, { color: focused ? value : void 0, bold: focused, dimColor: !focused, children: [
|
|
1892
|
+
focused ? glyphs().caretR : " ",
|
|
1893
|
+
" Accent color"
|
|
1894
|
+
] }) }),
|
|
1895
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1896
|
+
/* @__PURE__ */ jsxs6(Text6, { children: [
|
|
1897
|
+
" ",
|
|
1898
|
+
focused ? glyphs().vbar : " ",
|
|
1899
|
+
" "
|
|
1900
|
+
] }),
|
|
1901
|
+
COLOR_PALETTE.map((c) => /* @__PURE__ */ jsx7(Box6, { marginRight: 1, children: c === value ? /* @__PURE__ */ jsxs6(Text6, { bold: true, color: c, children: [
|
|
1902
|
+
"[",
|
|
1903
|
+
glyphs().dot,
|
|
1904
|
+
"]"
|
|
1905
|
+
] }) : /* @__PURE__ */ jsxs6(Text6, { color: c, dimColor: !focused, children: [
|
|
1906
|
+
" ",
|
|
1907
|
+
glyphs().dot
|
|
1908
|
+
] }) }, c))
|
|
1909
|
+
] }),
|
|
1910
|
+
/* @__PURE__ */ jsx7(Box6, { children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " shows on dashboard, account strip, borders" }) })
|
|
1911
|
+
] });
|
|
1912
|
+
}
|
|
1496
1913
|
|
|
1497
1914
|
// src/ui/app-layout.logic.ts
|
|
1498
1915
|
function deriveSlots(accounts, privacyMode = false) {
|
|
@@ -1518,32 +1935,33 @@ function computeChrome(slots, cols, rows) {
|
|
|
1518
1935
|
|
|
1519
1936
|
// src/ui/resizing.tsx
|
|
1520
1937
|
import { Box as Box7, Text as Text7 } from "ink";
|
|
1521
|
-
import { jsx as
|
|
1938
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1522
1939
|
function ResizingView({ cols, rows }) {
|
|
1523
|
-
|
|
1940
|
+
const theme = useTuiTheme();
|
|
1941
|
+
return /* @__PURE__ */ jsx8(Box7, { width: cols, height: rows, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
1524
1942
|
glyphs().dotSel,
|
|
1525
1943
|
" resizing\u2026 ",
|
|
1526
|
-
/* @__PURE__ */
|
|
1944
|
+
/* @__PURE__ */ jsx8(Text7, { color: theme.accent, children: cols }),
|
|
1527
1945
|
"\xD7",
|
|
1528
|
-
/* @__PURE__ */
|
|
1946
|
+
/* @__PURE__ */ jsx8(Text7, { color: theme.accent, children: rows })
|
|
1529
1947
|
] }) });
|
|
1530
1948
|
}
|
|
1531
1949
|
|
|
1532
1950
|
// src/ui/account-strip.tsx
|
|
1533
1951
|
import { memo as memo5 } from "react";
|
|
1534
1952
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
1535
|
-
import { jsx as
|
|
1953
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1536
1954
|
var AccountStrip = memo5(function AccountStrip2({ slots, activeIdx, onSelect }) {
|
|
1537
|
-
return /* @__PURE__ */
|
|
1955
|
+
return /* @__PURE__ */ jsx9(Box8, { flexWrap: "wrap", children: slots.map((s, i) => {
|
|
1538
1956
|
const active = i === activeIdx;
|
|
1539
1957
|
const dot = s.id === null ? glyphs().dotAll : glyphs().dot;
|
|
1540
1958
|
const label = truncateName(s.name, 16);
|
|
1541
1959
|
return /* @__PURE__ */ jsxs8(ClickableBox, { onClick: () => onSelect(i), marginRight: 2, children: [
|
|
1542
|
-
/* @__PURE__ */
|
|
1543
|
-
/* @__PURE__ */
|
|
1544
|
-
/* @__PURE__ */
|
|
1545
|
-
/* @__PURE__ */
|
|
1546
|
-
active ? /* @__PURE__ */
|
|
1960
|
+
/* @__PURE__ */ jsx9(Text8, { dimColor: !active, children: i }),
|
|
1961
|
+
/* @__PURE__ */ jsx9(Text8, { children: " " }),
|
|
1962
|
+
/* @__PURE__ */ jsx9(Text8, { color: s.color, bold: active, dimColor: !active, children: dot }),
|
|
1963
|
+
/* @__PURE__ */ jsx9(Text8, { children: " " }),
|
|
1964
|
+
active ? /* @__PURE__ */ jsx9(Text8, { bold: true, color: s.color, children: label }) : /* @__PURE__ */ jsx9(Text8, { dimColor: true, children: label })
|
|
1547
1965
|
] }, s.id ?? "__all__");
|
|
1548
1966
|
}) });
|
|
1549
1967
|
});
|
|
@@ -1596,7 +2014,7 @@ function osc8(text, url) {
|
|
|
1596
2014
|
}
|
|
1597
2015
|
|
|
1598
2016
|
// src/ui/footer.tsx
|
|
1599
|
-
import { jsx as
|
|
2017
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1600
2018
|
var Footer = memo6(function Footer2({ hasAccounts, paginated, cols }) {
|
|
1601
2019
|
const inner = cols - 4;
|
|
1602
2020
|
const BASE = "by David Ilie (davidilie.com) \xB7 r/R=refresh O=repo W=web s=settings q=quit".length;
|
|
@@ -1608,22 +2026,22 @@ var Footer = memo6(function Footer2({ hasAccounts, paginated, cols }) {
|
|
|
1608
2026
|
const showJump = hasAccounts && inner >= BASE + (showOpt ? OPT : 0) + JUMP + (paginated ? PAGE : 0);
|
|
1609
2027
|
const showPage = paginated && inner >= BASE + (showOpt ? OPT : 0) + (showJump ? JUMP : 0) + PAGE;
|
|
1610
2028
|
if (inner < BASE) {
|
|
1611
|
-
return /* @__PURE__ */
|
|
2029
|
+
return /* @__PURE__ */ jsx10(Box9, { marginTop: 1, flexWrap: "nowrap", children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, wrap: "truncate-end", children: "r/R=refresh O=repo W=web s=settings q=quit" }) });
|
|
1612
2030
|
}
|
|
1613
2031
|
return /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexWrap: "nowrap", children: [
|
|
1614
|
-
/* @__PURE__ */
|
|
1615
|
-
/* @__PURE__ */
|
|
1616
|
-
/* @__PURE__ */
|
|
1617
|
-
/* @__PURE__ */
|
|
2032
|
+
/* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "by " }),
|
|
2033
|
+
/* @__PURE__ */ jsx10(LinkBox, { onClick: () => openUrl(REPO_URL), children: /* @__PURE__ */ jsx10(Transform, { transform: (s) => osc8(s, REPO_URL), children: /* @__PURE__ */ jsx10(Text9, { underline: true, children: "David Ilie" }) }) }),
|
|
2034
|
+
/* @__PURE__ */ jsx10(Text9, { dimColor: true, children: " (" }),
|
|
2035
|
+
/* @__PURE__ */ jsx10(LinkBox, { onClick: () => openUrl(SITE_URL), children: /* @__PURE__ */ jsx10(Transform, { transform: (s) => osc8(s, SITE_URL), children: /* @__PURE__ */ jsx10(Text9, { color: "cyan", underline: true, children: "davidilie.com" }) }) }),
|
|
1618
2036
|
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
1619
2037
|
") ",
|
|
1620
2038
|
glyphs().middot,
|
|
1621
2039
|
" r/R=refresh O=repo W=web s=settings "
|
|
1622
2040
|
] }),
|
|
1623
|
-
showOpt && /* @__PURE__ */
|
|
1624
|
-
showJump && /* @__PURE__ */
|
|
1625
|
-
showPage && /* @__PURE__ */
|
|
1626
|
-
/* @__PURE__ */
|
|
2041
|
+
showOpt && /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: optHint }),
|
|
2042
|
+
showJump && /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "0-9=jump a/A=cycle " }),
|
|
2043
|
+
showPage && /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "scroll=page " }),
|
|
2044
|
+
/* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "q=quit" })
|
|
1627
2045
|
] });
|
|
1628
2046
|
});
|
|
1629
2047
|
|
|
@@ -1633,16 +2051,17 @@ import { Box as Box10, Text as Text11 } from "ink";
|
|
|
1633
2051
|
// src/ui/refresh-status.tsx
|
|
1634
2052
|
import { memo as memo7 } from "react";
|
|
1635
2053
|
import { Text as Text10 } from "ink";
|
|
1636
|
-
import { jsx as
|
|
2054
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1637
2055
|
var RefreshStatusLine = memo7(function RefreshStatusLine2({ status }) {
|
|
2056
|
+
const theme = useTuiTheme();
|
|
1638
2057
|
if (status.phase === "idle") return null;
|
|
1639
|
-
if (status.phase === "refreshing") return /* @__PURE__ */
|
|
1640
|
-
if (status.phase === "success") return /* @__PURE__ */ jsxs10(Text10, { color:
|
|
2058
|
+
if (status.phase === "refreshing") return /* @__PURE__ */ jsx11(Spinner, { label: `${status.message}${glyphs().ellipsis}` });
|
|
2059
|
+
if (status.phase === "success") return /* @__PURE__ */ jsxs10(Text10, { color: theme.ok, children: [
|
|
1641
2060
|
glyphs().check,
|
|
1642
2061
|
" ",
|
|
1643
2062
|
status.message
|
|
1644
2063
|
] });
|
|
1645
|
-
return /* @__PURE__ */ jsxs10(Text10, { color:
|
|
2064
|
+
return /* @__PURE__ */ jsxs10(Text10, { color: theme.crit, children: [
|
|
1646
2065
|
glyphs().warn,
|
|
1647
2066
|
" ",
|
|
1648
2067
|
status.message
|
|
@@ -1650,14 +2069,15 @@ var RefreshStatusLine = memo7(function RefreshStatusLine2({ status }) {
|
|
|
1650
2069
|
});
|
|
1651
2070
|
|
|
1652
2071
|
// src/ui/tiny-fallback.tsx
|
|
1653
|
-
import { jsx as
|
|
2072
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1654
2073
|
function TinyFallback({ groups, stats, rows, cols, refreshStatus }) {
|
|
2074
|
+
const theme = useTuiTheme();
|
|
1655
2075
|
const maxLines = Math.max(1, rows - 4);
|
|
1656
2076
|
const visible = groups.slice(0, maxLines);
|
|
1657
2077
|
const hidden = groups.length - visible.length;
|
|
1658
2078
|
const w = Math.max(8, cols - 2);
|
|
1659
2079
|
return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", paddingX: 1, height: rows, overflow: "hidden", children: [
|
|
1660
|
-
/* @__PURE__ */ jsxs11(Text11, { bold: true, color:
|
|
2080
|
+
/* @__PURE__ */ jsxs11(Text11, { bold: true, color: theme.accent, children: [
|
|
1661
2081
|
glyphs().dotSel,
|
|
1662
2082
|
" tokmon"
|
|
1663
2083
|
] }),
|
|
@@ -1665,15 +2085,15 @@ function TinyFallback({ groups, stats, rows, cols, refreshStatus }) {
|
|
|
1665
2085
|
"No providers ",
|
|
1666
2086
|
glyphs().emDash,
|
|
1667
2087
|
" s=settings"
|
|
1668
|
-
] }) : visible.map((g) => /* @__PURE__ */
|
|
2088
|
+
] }) : visible.map((g) => /* @__PURE__ */ jsx12(TinyRow, { provider: g.provider, accounts: g.accounts, stats, width: w }, g.provider)),
|
|
1669
2089
|
hidden > 0 && /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
1670
2090
|
"+",
|
|
1671
2091
|
hidden,
|
|
1672
2092
|
" more (enlarge terminal)"
|
|
1673
2093
|
] }),
|
|
1674
|
-
/* @__PURE__ */
|
|
1675
|
-
/* @__PURE__ */
|
|
1676
|
-
/* @__PURE__ */
|
|
2094
|
+
/* @__PURE__ */ jsx12(RefreshStatusLine, { status: refreshStatus }),
|
|
2095
|
+
/* @__PURE__ */ jsx12(Box10, { flexGrow: 1 }),
|
|
2096
|
+
/* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "R=refresh s=settings q=quit" })
|
|
1677
2097
|
] });
|
|
1678
2098
|
}
|
|
1679
2099
|
function TinyRow({ provider, accounts, stats, width }) {
|
|
@@ -1689,19 +2109,19 @@ function TinyRow({ provider, accounts, stats, width }) {
|
|
|
1689
2109
|
glyphs().dot,
|
|
1690
2110
|
" "
|
|
1691
2111
|
] }),
|
|
1692
|
-
/* @__PURE__ */
|
|
1693
|
-
/* @__PURE__ */
|
|
1694
|
-
/* @__PURE__ */
|
|
2112
|
+
/* @__PURE__ */ jsx12(Text11, { bold: true, color: meta.color, children: name }),
|
|
2113
|
+
/* @__PURE__ */ jsx12(Box10, { flexGrow: 1 }),
|
|
2114
|
+
/* @__PURE__ */ jsx12(Text11, { dimColor: true, children: detail })
|
|
1695
2115
|
] });
|
|
1696
2116
|
}
|
|
1697
2117
|
|
|
1698
2118
|
// src/client/use-daemon.ts
|
|
1699
|
-
import { useEffect as useEffect3, useMemo, useRef as useRef2, useState as useState3 } from "react";
|
|
2119
|
+
import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
|
|
1700
2120
|
function useDaemon(baseUrl) {
|
|
1701
2121
|
const [snapshot, setSnapshot] = useState3(null);
|
|
1702
2122
|
const [conn, setConn] = useState3("connecting");
|
|
1703
2123
|
const [config, setConfigState] = useState3(null);
|
|
1704
|
-
const client =
|
|
2124
|
+
const client = useMemo2(() => {
|
|
1705
2125
|
if (!baseUrl) return null;
|
|
1706
2126
|
return createDaemonRpcClient(baseUrl, {
|
|
1707
2127
|
transport: "node",
|
|
@@ -1744,6 +2164,7 @@ function indexById(snapshot) {
|
|
|
1744
2164
|
}
|
|
1745
2165
|
function toStatsMap(snapshot, accounts) {
|
|
1746
2166
|
const byId = indexById(snapshot);
|
|
2167
|
+
const providerHeadroom = new Map(snapshot?.providers.map((provider) => [provider.id, provider.headroom]) ?? []);
|
|
1747
2168
|
const out = /* @__PURE__ */ new Map();
|
|
1748
2169
|
for (const account of accounts) {
|
|
1749
2170
|
const wa = byId.get(account.id);
|
|
@@ -1751,7 +2172,11 @@ function toStatsMap(snapshot, accounts) {
|
|
|
1751
2172
|
account,
|
|
1752
2173
|
dashboard: wa?.dashboard ?? null,
|
|
1753
2174
|
billing: wa?.billing ?? null,
|
|
1754
|
-
billingUpdatedAt: wa?.billingUpdatedAt ?? null
|
|
2175
|
+
billingUpdatedAt: wa?.billingUpdatedAt ?? null,
|
|
2176
|
+
identity: wa?.identity,
|
|
2177
|
+
quotas: wa?.quotas,
|
|
2178
|
+
headroom: wa?.headroom,
|
|
2179
|
+
providerHeadroom: providerHeadroom.get(account.providerId)
|
|
1755
2180
|
});
|
|
1756
2181
|
}
|
|
1757
2182
|
return out;
|
|
@@ -1777,13 +2202,13 @@ var SORTS = [
|
|
|
1777
2202
|
{ label: "cost", dir: "down" }
|
|
1778
2203
|
];
|
|
1779
2204
|
var acctKey = (a) => `${a.id}:${a.homeDir ?? ""}`;
|
|
1780
|
-
var clampCaret = (
|
|
1781
|
-
function spliceInsert(value,
|
|
1782
|
-
const c = clampCaret(
|
|
2205
|
+
var clampCaret = (caret2, len) => Math.max(0, Math.min(caret2, len));
|
|
2206
|
+
function spliceInsert(value, caret2, text) {
|
|
2207
|
+
const c = clampCaret(caret2, value.length);
|
|
1783
2208
|
return { value: value.slice(0, c) + text + value.slice(c), caret: c + text.length };
|
|
1784
2209
|
}
|
|
1785
|
-
function spliceBackspace(value,
|
|
1786
|
-
const c = clampCaret(
|
|
2210
|
+
function spliceBackspace(value, caret2) {
|
|
2211
|
+
const c = clampCaret(caret2, value.length);
|
|
1787
2212
|
if (c === 0) return { value, caret: 0 };
|
|
1788
2213
|
return { value: value.slice(0, c - 1) + value.slice(c), caret: c - 1 };
|
|
1789
2214
|
}
|
|
@@ -1983,11 +2408,11 @@ function handleTimezoneEditor(input, key, editor, text, updateConfig) {
|
|
|
1983
2408
|
return;
|
|
1984
2409
|
}
|
|
1985
2410
|
if (key.leftArrow) {
|
|
1986
|
-
setCaret((
|
|
2411
|
+
setCaret((caret2) => clampCaret(caret2 - 1, value.length));
|
|
1987
2412
|
return;
|
|
1988
2413
|
}
|
|
1989
2414
|
if (key.rightArrow) {
|
|
1990
|
-
setCaret((
|
|
2415
|
+
setCaret((caret2) => clampCaret(caret2 + 1, value.length));
|
|
1991
2416
|
return;
|
|
1992
2417
|
}
|
|
1993
2418
|
if (key.ctrl && input === "a") {
|
|
@@ -2033,11 +2458,11 @@ function handleAllowedHostsEditor(input, key, editor, text, updateConfig) {
|
|
|
2033
2458
|
return;
|
|
2034
2459
|
}
|
|
2035
2460
|
if (key.leftArrow) {
|
|
2036
|
-
setCaret((
|
|
2461
|
+
setCaret((caret2) => clampCaret(caret2 - 1, value.length));
|
|
2037
2462
|
return;
|
|
2038
2463
|
}
|
|
2039
2464
|
if (key.rightArrow) {
|
|
2040
|
-
setCaret((
|
|
2465
|
+
setCaret((caret2) => clampCaret(caret2 + 1, value.length));
|
|
2041
2466
|
return;
|
|
2042
2467
|
}
|
|
2043
2468
|
if (key.ctrl && input === "a") {
|
|
@@ -2073,11 +2498,11 @@ function handleSearchEditor(input, key, table, text) {
|
|
|
2073
2498
|
return;
|
|
2074
2499
|
}
|
|
2075
2500
|
if (key.leftArrow) {
|
|
2076
|
-
setSearchCaret((
|
|
2501
|
+
setSearchCaret((caret2) => clampCaret(caret2 - 1, search.length));
|
|
2077
2502
|
return;
|
|
2078
2503
|
}
|
|
2079
2504
|
if (key.rightArrow) {
|
|
2080
|
-
setSearchCaret((
|
|
2505
|
+
setSearchCaret((caret2) => clampCaret(caret2 + 1, search.length));
|
|
2081
2506
|
return;
|
|
2082
2507
|
}
|
|
2083
2508
|
if (key.ctrl && input === "a") {
|
|
@@ -2137,7 +2562,7 @@ function handleSettings(input, key, ctx) {
|
|
|
2137
2562
|
ctx.allowedHostsEditor.setValue(null);
|
|
2138
2563
|
ctx.allowedHostsEditor.setError(null);
|
|
2139
2564
|
};
|
|
2140
|
-
const rowCount = tab === "general" ? GENERAL_ROWS : tab === "providers" ? PROVIDER_ORDER.length : trackedAccounts.length + 1;
|
|
2565
|
+
const rowCount = tab === "general" ? GENERAL_ROWS : tab === "theme" ? THEME_ROWS : tab === "desktop" ? DESKTOP_FIXED_ROWS + PROVIDER_ORDER.length : tab === "providers" ? PROVIDER_ORDER.length + 1 : trackedAccounts.length + 1;
|
|
2141
2566
|
if (key.tab) {
|
|
2142
2567
|
switchTab(key.shift ? -1 : 1);
|
|
2143
2568
|
return;
|
|
@@ -2183,17 +2608,64 @@ function handleSettings(input, key, ctx) {
|
|
|
2183
2608
|
return;
|
|
2184
2609
|
}
|
|
2185
2610
|
if (tab === "general") {
|
|
2186
|
-
|
|
2611
|
+
GENERAL_SETTINGS[cursor]?.onAdjust(input, key, ctx);
|
|
2612
|
+
return;
|
|
2613
|
+
}
|
|
2614
|
+
if (tab === "theme") {
|
|
2615
|
+
THEME_SETTINGS[cursor]?.onAdjust(input, key, ctx);
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
if (tab === "desktop") {
|
|
2619
|
+
if (cursor < DESKTOP_FIXED_ROWS) {
|
|
2620
|
+
DESKTOP_FIXED_SETTINGS[cursor]?.onAdjust(input, key, ctx);
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
const provider = PROVIDER_ORDER[cursor - DESKTOP_FIXED_ROWS];
|
|
2624
|
+
if (provider && (input === " " || key.leftArrow || key.rightArrow || key.return)) {
|
|
2625
|
+
updateConfig((current) => ({
|
|
2626
|
+
...current,
|
|
2627
|
+
tray: {
|
|
2628
|
+
...current.tray,
|
|
2629
|
+
pinnedProviders: toggleProviderSelection(current.tray.pinnedProviders, provider, new Set(PROVIDER_ORDER), 2)
|
|
2630
|
+
}
|
|
2631
|
+
}));
|
|
2632
|
+
}
|
|
2187
2633
|
return;
|
|
2188
2634
|
}
|
|
2189
2635
|
if (tab === "providers") {
|
|
2190
|
-
if (cursor
|
|
2191
|
-
|
|
2636
|
+
if (cursor === 0 && (input === " " || key.return || key.leftArrow || key.rightArrow)) {
|
|
2637
|
+
updateConfig((current) => ({
|
|
2638
|
+
...current,
|
|
2639
|
+
accountDetection: { ...current.accountDetection, enabled: !current.accountDetection.enabled }
|
|
2640
|
+
}));
|
|
2641
|
+
return;
|
|
2642
|
+
}
|
|
2643
|
+
const provider = PROVIDER_ORDER[cursor - 1];
|
|
2644
|
+
if (provider && input === "a") {
|
|
2645
|
+
updateConfig((current) => ({
|
|
2646
|
+
...current,
|
|
2647
|
+
accountDetection: setProviderDetectionEnabled(
|
|
2648
|
+
current.accountDetection,
|
|
2649
|
+
provider,
|
|
2650
|
+
!providerDetectionEnabled(current.accountDetection, provider)
|
|
2651
|
+
)
|
|
2652
|
+
}));
|
|
2653
|
+
return;
|
|
2654
|
+
}
|
|
2655
|
+
if (provider && (input === " " || key.return || key.leftArrow || key.rightArrow)) {
|
|
2656
|
+
toggleProvider(provider);
|
|
2192
2657
|
}
|
|
2193
2658
|
return;
|
|
2194
2659
|
}
|
|
2195
2660
|
if (cursor < trackedAccounts.length) {
|
|
2196
2661
|
const row = trackedAccounts[cursor];
|
|
2662
|
+
if (row.source === "ignored" && (key.return || input === "x")) {
|
|
2663
|
+
if (row.excludedRef) updateConfig((current) => ({
|
|
2664
|
+
...current,
|
|
2665
|
+
accountDetection: setDetectedAccountExcluded(current.accountDetection, row.excludedRef, false)
|
|
2666
|
+
}));
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2197
2669
|
if (key.return) {
|
|
2198
2670
|
if (row.source === "configured") {
|
|
2199
2671
|
const account = config.accounts.find((candidate) => candidate.id === row.explicitId);
|
|
@@ -2207,6 +2679,17 @@ function handleSettings(input, key, ctx) {
|
|
|
2207
2679
|
deleteAccount(row.explicitId);
|
|
2208
2680
|
return;
|
|
2209
2681
|
}
|
|
2682
|
+
if (row.source === "auto" && input === "x") {
|
|
2683
|
+
updateConfig((current) => ({
|
|
2684
|
+
...current,
|
|
2685
|
+
activeAccountId: current.activeAccountId === row.id ? null : current.activeAccountId,
|
|
2686
|
+
accountDetection: setDetectedAccountExcluded(current.accountDetection, {
|
|
2687
|
+
providerId: row.providerId,
|
|
2688
|
+
homeDir: row.homeDir
|
|
2689
|
+
}, true)
|
|
2690
|
+
}));
|
|
2691
|
+
return;
|
|
2692
|
+
}
|
|
2210
2693
|
if (input === " ") {
|
|
2211
2694
|
updateConfig((current) => ({ ...current, activeAccountId: row.id }));
|
|
2212
2695
|
return;
|
|
@@ -2214,69 +2697,6 @@ function handleSettings(input, key, ctx) {
|
|
|
2214
2697
|
return;
|
|
2215
2698
|
}
|
|
2216
2699
|
if (cursor === trackedAccounts.length && key.return) openAddAccount();
|
|
2217
|
-
function handleGeneralSetting(value, inputKey, context) {
|
|
2218
|
-
const index = context.settings.cursor;
|
|
2219
|
-
if (index === 0) {
|
|
2220
|
-
if (inputKey.leftArrow) updateConfig((current) => ({ ...current, interval: Math.max(1, current.interval - 1) }));
|
|
2221
|
-
if (inputKey.rightArrow) updateConfig((current) => ({ ...current, interval: current.interval + 1 }));
|
|
2222
|
-
return;
|
|
2223
|
-
}
|
|
2224
|
-
if (index === 1) {
|
|
2225
|
-
if (inputKey.leftArrow) updateConfig((current) => ({ ...current, billingInterval: Math.max(1, current.billingInterval - 1) }));
|
|
2226
|
-
if (inputKey.rightArrow) updateConfig((current) => ({ ...current, billingInterval: current.billingInterval + 1 }));
|
|
2227
|
-
return;
|
|
2228
|
-
}
|
|
2229
|
-
if (index === 2 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2230
|
-
updateConfig((current) => ({ ...current, clearScreen: !current.clearScreen }));
|
|
2231
|
-
return;
|
|
2232
|
-
}
|
|
2233
|
-
if (index === 3 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2234
|
-
updateConfig((current) => ({ ...current, privacyMode: !current.privacyMode }));
|
|
2235
|
-
return;
|
|
2236
|
-
}
|
|
2237
|
-
if (index === 4) {
|
|
2238
|
-
if (textInput.isPrintable(value, inputKey)) {
|
|
2239
|
-
const clean = sanitizeTyped(value);
|
|
2240
|
-
if (clean.length === 1) updateConfig((current) => ({ ...current, privacyToggleKey: clean }));
|
|
2241
|
-
}
|
|
2242
|
-
if (inputKey.backspace || inputKey.delete) updateConfig((current) => ({ ...current, privacyToggleKey: "p" }));
|
|
2243
|
-
return;
|
|
2244
|
-
}
|
|
2245
|
-
if (index === 5) {
|
|
2246
|
-
if (inputKey.return) {
|
|
2247
|
-
const initial = config.timezone ?? "";
|
|
2248
|
-
timezoneEditor.setValue(initial);
|
|
2249
|
-
timezoneEditor.setCaret(initial.length);
|
|
2250
|
-
timezoneEditor.setError(null);
|
|
2251
|
-
}
|
|
2252
|
-
if (inputKey.leftArrow || inputKey.rightArrow) {
|
|
2253
|
-
updateConfig((current) => ({ ...current, timezone: current.timezone === null ? systemTimezone() : null }));
|
|
2254
|
-
}
|
|
2255
|
-
return;
|
|
2256
|
-
}
|
|
2257
|
-
if (index === 6 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2258
|
-
updateConfig((current) => ({ ...current, dashboardLayout: current.dashboardLayout === "grid" ? "single" : "grid" }));
|
|
2259
|
-
return;
|
|
2260
|
-
}
|
|
2261
|
-
if (index === 7 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2262
|
-
updateConfig((current) => ({ ...current, defaultFocus: current.defaultFocus === "all" ? "last" : "all" }));
|
|
2263
|
-
return;
|
|
2264
|
-
}
|
|
2265
|
-
if (index === 8 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2266
|
-
updateConfig((current) => ({ ...current, allowNetworkAccess: !current.allowNetworkAccess }));
|
|
2267
|
-
return;
|
|
2268
|
-
}
|
|
2269
|
-
if (index === 9 && inputKey.return) {
|
|
2270
|
-
const initial = config.allowedHosts.join(", ");
|
|
2271
|
-
context.allowedHostsEditor.setValue(initial);
|
|
2272
|
-
context.allowedHostsEditor.setCaret(initial.length);
|
|
2273
|
-
context.allowedHostsEditor.setError(null);
|
|
2274
|
-
return;
|
|
2275
|
-
}
|
|
2276
|
-
if (index === 10 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2277
|
-
updateConfig((current) => ({ ...current, resetDisplay: current.resetDisplay === "relative" ? "absolute" : "relative" }));
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
2700
|
}
|
|
2281
2701
|
|
|
2282
2702
|
// src/ui/keybindings-navigation.ts
|
|
@@ -2345,7 +2765,8 @@ function handleNavigation(input, key, ctx) {
|
|
|
2345
2765
|
}
|
|
2346
2766
|
if (table.tab !== 1) {
|
|
2347
2767
|
if (key.leftArrow || key.rightArrow) {
|
|
2348
|
-
|
|
2768
|
+
const step2 = key.leftArrow ? -1 : 1;
|
|
2769
|
+
global.setTab((current) => (current + step2 + TABS.length) % TABS.length);
|
|
2349
2770
|
resetView();
|
|
2350
2771
|
}
|
|
2351
2772
|
return;
|
|
@@ -3148,8 +3569,8 @@ function useAccountForm({ cfg, detected, updateConfig, trackedAccountRows, setSe
|
|
|
3148
3569
|
if (!f) return f;
|
|
3149
3570
|
const i = FORM_FIELDS.indexOf(f.field);
|
|
3150
3571
|
const next = FORM_FIELDS[(i + dir + FORM_FIELDS.length) % FORM_FIELDS.length];
|
|
3151
|
-
const
|
|
3152
|
-
return { ...f, field: next, caret };
|
|
3572
|
+
const caret2 = next === "name" ? f.name.length : next === "homeDir" ? f.homeDir.length : f.caret;
|
|
3573
|
+
return { ...f, field: next, caret: caret2 };
|
|
3153
3574
|
});
|
|
3154
3575
|
}, []);
|
|
3155
3576
|
const cycleProvider = useCallback6((dir) => {
|
|
@@ -3200,7 +3621,7 @@ function useAccountForm({ cfg, detected, updateConfig, trackedAccountRows, setSe
|
|
|
3200
3621
|
}
|
|
3201
3622
|
|
|
3202
3623
|
// src/app.tsx
|
|
3203
|
-
import { Fragment as
|
|
3624
|
+
import { Fragment as Fragment6, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3204
3625
|
function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "degraded" }) {
|
|
3205
3626
|
const connected = mode === "connected" && baseUrl !== null;
|
|
3206
3627
|
const degraded = !connected;
|
|
@@ -3246,9 +3667,24 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3246
3667
|
const billingMs = cfg.billingInterval * 6e4;
|
|
3247
3668
|
const tz = resolveTimezone(cfg.timezone);
|
|
3248
3669
|
const configReady = config !== null;
|
|
3249
|
-
const
|
|
3250
|
-
const
|
|
3251
|
-
|
|
3670
|
+
const snapshot = daemon.snapshot;
|
|
3671
|
+
const daemonDetected = useMemo3(
|
|
3672
|
+
() => [...new Set((snapshot?.accounts ?? []).map((account) => account.providerId))],
|
|
3673
|
+
[snapshot]
|
|
3674
|
+
);
|
|
3675
|
+
const detectedProviders = connected ? daemonDetected : detected;
|
|
3676
|
+
const accounts = useMemo3(() => connected ? (snapshot?.accounts ?? []).map((account) => ({
|
|
3677
|
+
id: account.id,
|
|
3678
|
+
providerId: account.providerId,
|
|
3679
|
+
name: account.name,
|
|
3680
|
+
color: account.color,
|
|
3681
|
+
homeDir: account.homeDir ?? void 0
|
|
3682
|
+
})) : buildAccounts(cfg, detectedProviders), [cfg, connected, detectedProviders, snapshot]);
|
|
3683
|
+
const trackedAccountRows = useMemo3(
|
|
3684
|
+
() => getTrackedAccountRows(cfg, detectedProviders, accounts),
|
|
3685
|
+
[cfg, detectedProviders, accounts]
|
|
3686
|
+
);
|
|
3687
|
+
const settingsRowCount = settingsTab === "general" ? GENERAL_ROWS : settingsTab === "theme" ? THEME_ROWS : settingsTab === "desktop" ? DESKTOP_FIXED_ROWS + PROVIDER_ORDER.length : settingsTab === "providers" ? PROVIDER_ORDER.length + 1 : trackedAccountRows.length + 1;
|
|
3252
3688
|
const accountsRef = useRef8([]);
|
|
3253
3689
|
accountsRef.current = accounts;
|
|
3254
3690
|
const rowCountRef = useRef8(0);
|
|
@@ -3261,19 +3697,19 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3261
3697
|
const allowedHostsCaretRef = useRef8(0);
|
|
3262
3698
|
const searchValueRef = useRef8("");
|
|
3263
3699
|
const searchCaretRef = useRef8(0);
|
|
3264
|
-
const accountsKey =
|
|
3265
|
-
const allGroups =
|
|
3266
|
-
const tableProvs =
|
|
3700
|
+
const accountsKey = useMemo3(() => accounts.map(acctKey).join("|"), [accounts]);
|
|
3701
|
+
const allGroups = useMemo3(() => accountsByProvider(accounts), [accounts]);
|
|
3702
|
+
const tableProvs = useMemo3(
|
|
3267
3703
|
() => allGroups.map((g) => g.provider).filter((pid) => PROVIDERS[pid].hasUsage),
|
|
3268
3704
|
[allGroups]
|
|
3269
3705
|
);
|
|
3270
3706
|
const effTableProvider = tableProvider && tableProvs.includes(tableProvider) ? tableProvider : tableProvs[0] ?? null;
|
|
3271
|
-
const tableAccounts =
|
|
3707
|
+
const tableAccounts = useMemo3(
|
|
3272
3708
|
() => effTableProvider ? accounts.filter((a) => a.providerId === effTableProvider) : [],
|
|
3273
3709
|
[accounts, effTableProvider]
|
|
3274
3710
|
);
|
|
3275
3711
|
const needsOnboarding = configReady && !cfg.onboarded;
|
|
3276
|
-
const newProviders = configReady && cfg.onboarded ? PROVIDER_ORDER.filter((p) => !cfg.knownProviders.includes(p) &&
|
|
3712
|
+
const newProviders = configReady && cfg.onboarded ? PROVIDER_ORDER.filter((p) => !cfg.knownProviders.includes(p) && detectedProviders.includes(p)) : [];
|
|
3277
3713
|
const showPicker = needsOnboarding || newProviders.length > 0;
|
|
3278
3714
|
const {
|
|
3279
3715
|
statsLocal,
|
|
@@ -3294,7 +3730,6 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3294
3730
|
activeTableProvider: effTableProvider,
|
|
3295
3731
|
tableVisible: tab === 1
|
|
3296
3732
|
});
|
|
3297
|
-
const snapshot = daemon.snapshot;
|
|
3298
3733
|
const daemonRefreshRef = useRef8(daemon.refresh);
|
|
3299
3734
|
const degradedRefreshRef = useRef8(refreshAllDegraded);
|
|
3300
3735
|
daemonRefreshRef.current = daemon.refresh;
|
|
@@ -3306,11 +3741,11 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3306
3741
|
requestDaemonRefresh,
|
|
3307
3742
|
requestDegradedRefresh
|
|
3308
3743
|
});
|
|
3309
|
-
const stats =
|
|
3744
|
+
const stats = useMemo3(
|
|
3310
3745
|
() => connected ? toStatsMap(snapshot, accounts) : statsLocal,
|
|
3311
3746
|
[connected, snapshot, accounts, statsLocal]
|
|
3312
3747
|
);
|
|
3313
|
-
const accountIdentities =
|
|
3748
|
+
const accountIdentities = useMemo3(() => {
|
|
3314
3749
|
const out = /* @__PURE__ */ new Map();
|
|
3315
3750
|
for (const [id, stat] of stats) {
|
|
3316
3751
|
const billing = stat.billing;
|
|
@@ -3325,7 +3760,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3325
3760
|
}, [stats]);
|
|
3326
3761
|
const showPeak = accounts.some((a) => a.providerId === "claude");
|
|
3327
3762
|
const peak = connected ? showPeak ? snapshot?.peak ?? null : null : peakLocal;
|
|
3328
|
-
const updated =
|
|
3763
|
+
const updated = useMemo3(
|
|
3329
3764
|
() => connected ? new Date(snapshot?.generatedAt ?? Date.now()) : updatedLocal,
|
|
3330
3765
|
[connected, snapshot, updatedLocal]
|
|
3331
3766
|
);
|
|
@@ -3339,23 +3774,23 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3339
3774
|
}
|
|
3340
3775
|
return statsReadyInput(statsLocal.get(id));
|
|
3341
3776
|
}, [connected, snapshot, statsLocal]);
|
|
3342
|
-
const slots =
|
|
3343
|
-
const { activeSlotIdx, focusId } =
|
|
3777
|
+
const slots = useMemo3(() => deriveSlots(accounts, cfg.privacyMode), [accounts, cfg.privacyMode]);
|
|
3778
|
+
const { activeSlotIdx, focusId } = useMemo3(
|
|
3344
3779
|
() => findActiveSlot(slots, cfg.activeAccountId),
|
|
3345
3780
|
[slots, cfg.activeAccountId]
|
|
3346
3781
|
);
|
|
3347
|
-
const visibleAccounts =
|
|
3782
|
+
const visibleAccounts = useMemo3(
|
|
3348
3783
|
() => focusId === null ? accounts : accounts.filter((a) => a.id === focusId),
|
|
3349
3784
|
[accounts, focusId]
|
|
3350
3785
|
);
|
|
3351
|
-
const groups =
|
|
3786
|
+
const groups = useMemo3(
|
|
3352
3787
|
() => focusId === null ? allGroups : accountsByProvider(visibleAccounts),
|
|
3353
3788
|
[allGroups, visibleAccounts, focusId]
|
|
3354
3789
|
);
|
|
3355
3790
|
const TOO_SMALL = cols < 40 || rows < 12;
|
|
3356
3791
|
const allReady = accounts.length > 0 && accounts.every((a) => accountReady(readyInputFor(a.id), a.providerId));
|
|
3357
|
-
const { gridBudget } =
|
|
3358
|
-
const dashLayout =
|
|
3792
|
+
const { gridBudget } = useMemo3(() => computeChrome(slots, cols, rows), [slots, cols, rows]);
|
|
3793
|
+
const dashLayout = useMemo3(
|
|
3359
3794
|
() => computeDashLayout(groups, stats, cols, gridBudget, focusId, cfg.dashboardLayout),
|
|
3360
3795
|
[groups, stats, cols, gridBudget, focusId, cfg.dashboardLayout]
|
|
3361
3796
|
);
|
|
@@ -3400,8 +3835,8 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3400
3835
|
};
|
|
3401
3836
|
const { handlePasteData, isPasteInput } = usePaste(insertText);
|
|
3402
3837
|
const SORTS_FOR = SORTS;
|
|
3403
|
-
const tableAccountIds =
|
|
3404
|
-
const table =
|
|
3838
|
+
const tableAccountIds = useMemo3(() => tableAccounts.map((a) => a.id), [tableAccounts]);
|
|
3839
|
+
const table = useMemo3(
|
|
3405
3840
|
() => connected ? pickTable(snapshot, tableAccountIds) : tableLocal,
|
|
3406
3841
|
[connected, snapshot, tableAccountIds, tableLocal]
|
|
3407
3842
|
);
|
|
@@ -3415,18 +3850,19 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3415
3850
|
accountsCount: accounts.length
|
|
3416
3851
|
});
|
|
3417
3852
|
const pickerProviders = needsOnboarding ? PROVIDER_ORDER : newProviders;
|
|
3418
|
-
const onboardEnabled = onboardSel ??
|
|
3853
|
+
const onboardEnabled = onboardSel ?? detectedProviders;
|
|
3419
3854
|
const onboardItems = pickerProviders.map((pid) => ({
|
|
3420
3855
|
id: pid,
|
|
3421
3856
|
name: PROVIDERS[pid].name,
|
|
3422
3857
|
color: PROVIDERS[pid].color,
|
|
3423
|
-
detected:
|
|
3858
|
+
detected: detectedProviders.includes(pid),
|
|
3424
3859
|
enabled: onboardEnabled.includes(pid)
|
|
3425
3860
|
}));
|
|
3426
3861
|
useEffect9(() => {
|
|
3862
|
+
if (connected) return;
|
|
3427
3863
|
void detectProviders().then(setDetected);
|
|
3428
|
-
}, []);
|
|
3429
|
-
const tableKey =
|
|
3864
|
+
}, [connected]);
|
|
3865
|
+
const tableKey = useMemo3(
|
|
3430
3866
|
() => `${effTableProvider}|${tableAccounts.map(acctKey).join(",")}|${tz}`,
|
|
3431
3867
|
[effTableProvider, tableAccounts, tz]
|
|
3432
3868
|
);
|
|
@@ -3484,7 +3920,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3484
3920
|
if (i < 0 || i >= pickerProviders.length) return;
|
|
3485
3921
|
const pid = pickerProviders[i];
|
|
3486
3922
|
setOnboardSel((prev) => {
|
|
3487
|
-
const base = prev ??
|
|
3923
|
+
const base = prev ?? detectedProviders;
|
|
3488
3924
|
return base.includes(pid) ? base.filter((p) => p !== pid) : [...base, pid];
|
|
3489
3925
|
});
|
|
3490
3926
|
}
|
|
@@ -3547,7 +3983,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3547
3983
|
cycleColor,
|
|
3548
3984
|
deleteAccount,
|
|
3549
3985
|
moveAccount
|
|
3550
|
-
} = useAccountForm({ cfg, detected, updateConfig, trackedAccountRows, setSettingsCursor });
|
|
3986
|
+
} = useAccountForm({ cfg, detected: detectedProviders, updateConfig, trackedAccountRows, setSettingsCursor });
|
|
3551
3987
|
async function toggleWeb() {
|
|
3552
3988
|
if (connected) {
|
|
3553
3989
|
if (baseUrl) openUrl(baseUrl);
|
|
@@ -3560,7 +3996,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3560
3996
|
if (webStartingRef.current) return;
|
|
3561
3997
|
webStartingRef.current = true;
|
|
3562
3998
|
try {
|
|
3563
|
-
const { startWebServer } = await import("./server-
|
|
3999
|
+
const { startWebServer } = await import("./server-EPSH52DR.js");
|
|
3564
4000
|
const ctrl = await startWebServer({ config: cfg, log: false });
|
|
3565
4001
|
webRef.current = ctrl;
|
|
3566
4002
|
openUrl(ctrl.url);
|
|
@@ -3590,11 +4026,11 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3590
4026
|
if (idx === cursor) setExpanded((e) => e === idx ? -1 : idx);
|
|
3591
4027
|
else setCursor(idx);
|
|
3592
4028
|
}, [cursor]);
|
|
3593
|
-
const rawTokenRows =
|
|
4029
|
+
const rawTokenRows = useMemo3(
|
|
3594
4030
|
() => tab === 1 ? table ? [table.daily, table.weekly, table.monthly][view] : [] : [],
|
|
3595
4031
|
[tab, table, view]
|
|
3596
4032
|
);
|
|
3597
|
-
const tokenModelOptions =
|
|
4033
|
+
const tokenModelOptions = useMemo3(() => tableModelOptions(rawTokenRows), [rawTokenRows]);
|
|
3598
4034
|
useEffect9(() => {
|
|
3599
4035
|
if (tableModel && !tokenModelOptions.includes(tableModel)) setTableModel(null);
|
|
3600
4036
|
}, [tableModel, tokenModelOptions]);
|
|
@@ -3603,7 +4039,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3603
4039
|
resetView();
|
|
3604
4040
|
}, [tokenModelOptions, resetView]);
|
|
3605
4041
|
const activeTableModel = tableModel && tokenModelOptions.includes(tableModel) ? tableModel : null;
|
|
3606
|
-
const tokenRows =
|
|
4042
|
+
const tokenRows = useMemo3(
|
|
3607
4043
|
() => tab === 1 ? sortRows(filterTokenRows(filterRowsByModel(rawTokenRows, activeTableModel), search), sort) : [],
|
|
3608
4044
|
[tab, rawTokenRows, activeTableModel, search, sort]
|
|
3609
4045
|
);
|
|
@@ -3695,10 +4131,10 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3695
4131
|
}
|
|
3696
4132
|
});
|
|
3697
4133
|
}, { isActive: IS_TTY });
|
|
3698
|
-
if (!config) return /* @__PURE__ */
|
|
3699
|
-
if (resizing) return /* @__PURE__ */
|
|
4134
|
+
if (!config) return /* @__PURE__ */ jsx13(Box11, { padding: 1, children: /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: "Loading..." }) });
|
|
4135
|
+
if (resizing) return /* @__PURE__ */ jsx13(TuiThemeProvider, { appearance: cfg.appearance, children: /* @__PURE__ */ jsx13(ResizingView, { cols: live.cols, rows: live.rows }) });
|
|
3700
4136
|
if (showPicker) {
|
|
3701
|
-
return /* @__PURE__ */
|
|
4137
|
+
return /* @__PURE__ */ jsx13(TuiThemeProvider, { appearance: cfg.appearance, children: /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", paddingX: 2, paddingY: 1, height: rows, children: /* @__PURE__ */ jsx13(
|
|
3702
4138
|
Onboarding,
|
|
3703
4139
|
{
|
|
3704
4140
|
items: onboardItems,
|
|
@@ -3708,22 +4144,19 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3708
4144
|
heading: needsOnboarding ? "Welcome to tokmon" : "New providers detected",
|
|
3709
4145
|
subheading: needsOnboarding ? "Pick the tools you want to track. You can change this anytime in settings." : "tokmon found these installed since you last set up. Pick which to track."
|
|
3710
4146
|
}
|
|
3711
|
-
) });
|
|
4147
|
+
) }) });
|
|
3712
4148
|
}
|
|
3713
4149
|
if (showLoader) {
|
|
3714
|
-
return /* @__PURE__ */
|
|
4150
|
+
return /* @__PURE__ */ jsx13(TuiThemeProvider, { appearance: cfg.appearance, children: /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", paddingX: 2, paddingY: 1, height: rows, overflow: "hidden", children: /* @__PURE__ */ jsx13(LoadingView, { groups: allGroups, stats, cols, rows, readyInput: readyInputFor, privacyMode: cfg.privacyMode }) }) });
|
|
3715
4151
|
}
|
|
3716
4152
|
if (TOO_SMALL && !showSettings) {
|
|
3717
|
-
return /* @__PURE__ */
|
|
4153
|
+
return /* @__PURE__ */ jsx13(TuiThemeProvider, { appearance: cfg.appearance, children: /* @__PURE__ */ jsx13(TinyFallback, { groups, stats, rows, cols, refreshStatus }) });
|
|
3718
4154
|
}
|
|
3719
4155
|
rowCountRef.current = tokenRows.length;
|
|
3720
|
-
return /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", paddingX: 2, paddingY: 1, height: rows, overflow: "hidden", children: [
|
|
4156
|
+
return /* @__PURE__ */ jsx13(TuiThemeProvider, { appearance: cfg.appearance, children: /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", paddingX: 2, paddingY: 1, height: rows, overflow: "hidden", children: [
|
|
3721
4157
|
/* @__PURE__ */ jsxs12(Box11, { justifyContent: "space-between", children: [
|
|
3722
4158
|
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
3723
|
-
/* @__PURE__ */
|
|
3724
|
-
glyphs().dotSel,
|
|
3725
|
-
" tokmon"
|
|
3726
|
-
] }),
|
|
4159
|
+
/* @__PURE__ */ jsx13(ThemedBrand, {}),
|
|
3727
4160
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
3728
4161
|
" ",
|
|
3729
4162
|
glyphs().middot,
|
|
@@ -3737,15 +4170,15 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3737
4170
|
] })
|
|
3738
4171
|
] }),
|
|
3739
4172
|
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
3740
|
-
peak && /* @__PURE__ */ jsxs12(
|
|
3741
|
-
/* @__PURE__ */
|
|
4173
|
+
peak && /* @__PURE__ */ jsxs12(Fragment6, { children: [
|
|
4174
|
+
/* @__PURE__ */ jsx13(PeakBadge, { peak, resetDisplay: cfg.resetDisplay, tz }),
|
|
3742
4175
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
3743
4176
|
" ",
|
|
3744
4177
|
glyphs().middot,
|
|
3745
4178
|
" "
|
|
3746
4179
|
] })
|
|
3747
4180
|
] }),
|
|
3748
|
-
/* @__PURE__ */
|
|
4181
|
+
/* @__PURE__ */ jsx13(Text12, { dimColor: true, children: time(updated, tz) })
|
|
3749
4182
|
] })
|
|
3750
4183
|
] }),
|
|
3751
4184
|
degraded && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
@@ -3760,19 +4193,19 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3760
4193
|
glyphs().middot,
|
|
3761
4194
|
" showing last known data"
|
|
3762
4195
|
] }),
|
|
3763
|
-
cfg.allowNetworkAccess && /* @__PURE__ */ jsxs12(
|
|
4196
|
+
cfg.allowNetworkAccess && /* @__PURE__ */ jsxs12(ThemedError, { children: [
|
|
3764
4197
|
glyphs().warn,
|
|
3765
4198
|
" unsafe network access enabled ",
|
|
3766
4199
|
glyphs().middot,
|
|
3767
4200
|
" dashboard may be reachable from your LAN after daemon restart"
|
|
3768
4201
|
] }),
|
|
3769
|
-
configSaveError && /* @__PURE__ */ jsxs12(
|
|
4202
|
+
configSaveError && /* @__PURE__ */ jsxs12(ThemedError, { children: [
|
|
3770
4203
|
glyphs().warn,
|
|
3771
4204
|
" ",
|
|
3772
4205
|
configSaveError
|
|
3773
4206
|
] }),
|
|
3774
|
-
/* @__PURE__ */
|
|
3775
|
-
showSettings ? /* @__PURE__ */
|
|
4207
|
+
/* @__PURE__ */ jsx13(RefreshStatusLine, { status: refreshStatus }),
|
|
4208
|
+
showSettings ? /* @__PURE__ */ jsx13(
|
|
3776
4209
|
SettingsView,
|
|
3777
4210
|
{
|
|
3778
4211
|
config: cfg,
|
|
@@ -3790,20 +4223,20 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3790
4223
|
trackedAccounts: trackedAccountRows,
|
|
3791
4224
|
accountIdentities
|
|
3792
4225
|
}
|
|
3793
|
-
) : /* @__PURE__ */ jsxs12(
|
|
4226
|
+
) : /* @__PURE__ */ jsxs12(Fragment6, { children: [
|
|
3794
4227
|
/* @__PURE__ */ jsxs12(Box11, { marginTop: 1, marginBottom: 1, children: [
|
|
3795
|
-
/* @__PURE__ */
|
|
4228
|
+
/* @__PURE__ */ jsx13(TabBar, { tabs: TABS, active: tab, onSelect: onTabSelect }),
|
|
3796
4229
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
3797
4230
|
" Tab/",
|
|
3798
4231
|
glyphs().arrowL,
|
|
3799
4232
|
glyphs().arrowR
|
|
3800
4233
|
] })
|
|
3801
4234
|
] }),
|
|
3802
|
-
tab === 0 && /* @__PURE__ */ jsxs12(
|
|
3803
|
-
/* @__PURE__ */
|
|
4235
|
+
tab === 0 && /* @__PURE__ */ jsxs12(Fragment6, { children: [
|
|
4236
|
+
/* @__PURE__ */ jsx13(DashboardView, { groups, stats, cols, budget: gridBudget, computed: dashLayout, page: dashPage, privacyMode: cfg.privacyMode, resetDisplay: cfg.resetDisplay, tz }),
|
|
3804
4237
|
slots.length > 1 && /* @__PURE__ */ jsxs12(Box11, { marginTop: 1, children: [
|
|
3805
|
-
/* @__PURE__ */
|
|
3806
|
-
/* @__PURE__ */
|
|
4238
|
+
/* @__PURE__ */ jsx13(Text12, { dimColor: true, children: "focus " }),
|
|
4239
|
+
/* @__PURE__ */ jsx13(
|
|
3807
4240
|
AccountStrip,
|
|
3808
4241
|
{
|
|
3809
4242
|
slots,
|
|
@@ -3812,12 +4245,12 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3812
4245
|
}
|
|
3813
4246
|
)
|
|
3814
4247
|
] }),
|
|
3815
|
-
/* @__PURE__ */
|
|
4248
|
+
/* @__PURE__ */ jsx13(TotalsRow, { groups, stats, cols })
|
|
3816
4249
|
] }),
|
|
3817
|
-
tab === 1 && /* @__PURE__ */ jsxs12(
|
|
3818
|
-
tableProvs.length > 0 && /* @__PURE__ */
|
|
3819
|
-
/* @__PURE__ */
|
|
3820
|
-
/* @__PURE__ */
|
|
4250
|
+
tab === 1 && /* @__PURE__ */ jsxs12(Fragment6, { children: [
|
|
4251
|
+
tableProvs.length > 0 && /* @__PURE__ */ jsx13(TableProviderBar, { providers: tableProvs, active: effTableProvider, onSelect: onProviderSelect }),
|
|
4252
|
+
/* @__PURE__ */ jsx13(Box11, { height: 1 }),
|
|
4253
|
+
/* @__PURE__ */ jsx13(
|
|
3821
4254
|
ControlBar,
|
|
3822
4255
|
{
|
|
3823
4256
|
views: VIEWS,
|
|
@@ -3831,12 +4264,12 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3831
4264
|
showModel: true
|
|
3832
4265
|
}
|
|
3833
4266
|
),
|
|
3834
|
-
/* @__PURE__ */
|
|
4267
|
+
/* @__PURE__ */ jsx13(Box11, { height: 1 }),
|
|
3835
4268
|
!effTableProvider ? /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
3836
4269
|
"No providers enabled ",
|
|
3837
4270
|
glyphs().emDash,
|
|
3838
4271
|
" press s to pick providers."
|
|
3839
|
-
] }) : tableLoading && !table ? /* @__PURE__ */
|
|
4272
|
+
] }) : tableLoading && !table ? /* @__PURE__ */ jsx13(Spinner, { label: "Loading history" }) : /* @__PURE__ */ jsx13(
|
|
3840
4273
|
TokenTable,
|
|
3841
4274
|
{
|
|
3842
4275
|
rows: tokenRows,
|
|
@@ -3849,12 +4282,23 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3849
4282
|
)
|
|
3850
4283
|
] })
|
|
3851
4284
|
] }),
|
|
3852
|
-
(tab === 0 || showSettings) && /* @__PURE__ */
|
|
4285
|
+
(tab === 0 || showSettings) && /* @__PURE__ */ jsx13(Footer, { hasAccounts: slots.length > 1, paginated: tab === 0 && dashPaginated, cols })
|
|
4286
|
+
] }) });
|
|
4287
|
+
}
|
|
4288
|
+
function ThemedBrand() {
|
|
4289
|
+
const theme = useTuiTheme();
|
|
4290
|
+
return /* @__PURE__ */ jsxs12(Text12, { bold: true, color: theme.accent, children: [
|
|
4291
|
+
glyphs().dotSel,
|
|
4292
|
+
" tokmon"
|
|
3853
4293
|
] });
|
|
3854
4294
|
}
|
|
4295
|
+
function ThemedError({ children }) {
|
|
4296
|
+
const theme = useTuiTheme();
|
|
4297
|
+
return /* @__PURE__ */ jsx13(Text12, { color: theme.crit, children });
|
|
4298
|
+
}
|
|
3855
4299
|
|
|
3856
4300
|
// src/bootstrap-ink.tsx
|
|
3857
|
-
import { jsx as
|
|
4301
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
3858
4302
|
function enterAltScreen() {
|
|
3859
4303
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
3860
4304
|
}
|
|
@@ -3883,7 +4327,7 @@ async function bootstrapInk({ interval, config, daemon, mode }) {
|
|
|
3883
4327
|
if (altScreen) leaveAltScreen();
|
|
3884
4328
|
});
|
|
3885
4329
|
const { waitUntilExit } = render(
|
|
3886
|
-
/* @__PURE__ */
|
|
4330
|
+
/* @__PURE__ */ jsx14(MouseProvider, { children: /* @__PURE__ */ jsx14(
|
|
3887
4331
|
App,
|
|
3888
4332
|
{
|
|
3889
4333
|
interval,
|