tokmon 0.28.2 → 0.28.4
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-IMT3S5HU.js → bootstrap-ink-WG4WG3R2.js} +1579 -1136
- package/dist/chunk-5B3XCEMC.js +294 -0
- package/dist/chunk-7CH6S7BR.js +447 -0
- package/dist/{chunk-LCAHTRGP.js → chunk-CFKUZCMB.js} +56 -29
- package/dist/{chunk-LKYOWBD4.js → chunk-FLANGRI6.js} +21 -42
- package/dist/chunk-HNWEJ6MS.js +1094 -0
- package/dist/{chunk-WUK3MQUB.js → chunk-JZSZEHVD.js} +167 -10
- package/dist/{chunk-6ZEW2M6P.js → chunk-P57DVFNH.js} +125 -25
- package/dist/chunk-TN6V7XSL.js +512 -0
- package/dist/chunk-UYPDMVW5.js +21 -0
- package/dist/{cli-command-DCENRCO3.js → cli-command-I4GRR3MF.js} +249 -15
- package/dist/cli.js +6 -6
- package/dist/{config-HXFJTNLM.js → config-3DGZ47F7.js} +23 -3
- package/dist/daemon-ZXV3V5CS.js +384 -0
- package/dist/daemon-handle-G4OGPFR3.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-44ZSQEOS.js +0 -299
- package/dist/chunk-HP5UZCXP.js +0 -436
- package/dist/chunk-J3JW3RFP.js +0 -196
- package/dist/chunk-M7XMV36F.js +0 -275
- package/dist/daemon-6Y4O7NXS.js +0 -241
- package/dist/daemon-handle-GVCYVDPO.js +0 -10
- package/dist/server-GXXOWUVZ.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,24 +28,33 @@ import {
|
|
|
24
28
|
time,
|
|
25
29
|
tokens,
|
|
26
30
|
withTimeout
|
|
27
|
-
} from "./chunk-
|
|
28
|
-
import "./chunk-
|
|
31
|
+
} from "./chunk-CFKUZCMB.js";
|
|
32
|
+
import "./chunk-JZSZEHVD.js";
|
|
29
33
|
import {
|
|
30
34
|
COLOR_PALETTE,
|
|
31
35
|
DEFAULTS,
|
|
36
|
+
DESKTOP_GRAPH_RANGES,
|
|
37
|
+
THEME_PRESET_IDS,
|
|
32
38
|
configLocation,
|
|
33
39
|
generateAccountId,
|
|
34
40
|
getTrackedAccountRows,
|
|
41
|
+
isDarkOnlyThemePreset,
|
|
35
42
|
isValidTimezone,
|
|
36
43
|
loadConfig,
|
|
37
44
|
normalizeAllowedHost,
|
|
38
45
|
normalizeConfig,
|
|
39
46
|
pickAccentColor,
|
|
47
|
+
providerDetectionEnabled,
|
|
40
48
|
redactEmail,
|
|
49
|
+
resolveTerminalTheme,
|
|
41
50
|
sanitizeTyped,
|
|
42
51
|
saveConfigSync,
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
setDetectedAccountExcluded,
|
|
53
|
+
setProviderDetectionEnabled,
|
|
54
|
+
snapshotCacheFile,
|
|
55
|
+
themePresetOption,
|
|
56
|
+
toggleProviderSelection
|
|
57
|
+
} from "./chunk-HNWEJ6MS.js";
|
|
45
58
|
import {
|
|
46
59
|
glyphs
|
|
47
60
|
} from "./chunk-RF4GGQGM.js";
|
|
@@ -51,7 +64,7 @@ import { render } from "ink";
|
|
|
51
64
|
import { MouseProvider } from "@zenobius/ink-mouse";
|
|
52
65
|
|
|
53
66
|
// src/app.tsx
|
|
54
|
-
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";
|
|
55
68
|
import { Box as Box11, Text as Text12, useInput, useApp } from "ink";
|
|
56
69
|
import { useMouse } from "@zenobius/ink-mouse";
|
|
57
70
|
|
|
@@ -60,7 +73,55 @@ import { appendFileSync } from "fs";
|
|
|
60
73
|
import { memo, useEffect, useRef, useState } from "react";
|
|
61
74
|
import { Box, Text } from "ink";
|
|
62
75
|
import { useOnMouseClick } from "@zenobius/ink-mouse";
|
|
63
|
-
|
|
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";
|
|
64
125
|
function truncateName(s, n) {
|
|
65
126
|
const ell = glyphs().ellipsis;
|
|
66
127
|
return s.length > n ? s.slice(0, n - ell.length) + ell : s;
|
|
@@ -70,7 +131,7 @@ function ClickableBox({ onClick, children, ...props }) {
|
|
|
70
131
|
useOnMouseClick(ref, (clicked) => {
|
|
71
132
|
if (clicked) onClick();
|
|
72
133
|
});
|
|
73
|
-
return /* @__PURE__ */
|
|
134
|
+
return /* @__PURE__ */ jsx2(Box, { ref, ...props, children });
|
|
74
135
|
}
|
|
75
136
|
var SGR_PRESS = /\x1b\[<(\d+);(\d+);(\d+)M/g;
|
|
76
137
|
var linkHits = /* @__PURE__ */ new Set();
|
|
@@ -138,9 +199,10 @@ function LinkBox({ onClick, children, ...props }) {
|
|
|
138
199
|
linkHits.delete(hit);
|
|
139
200
|
};
|
|
140
201
|
}, []);
|
|
141
|
-
return /* @__PURE__ */
|
|
202
|
+
return /* @__PURE__ */ jsx2(Box, { ref, ...props, children });
|
|
142
203
|
}
|
|
143
204
|
function Spinner({ label }) {
|
|
205
|
+
const theme = useTuiTheme();
|
|
144
206
|
const frames = glyphs().spinner;
|
|
145
207
|
const [i, setI] = useState(0);
|
|
146
208
|
useEffect(() => {
|
|
@@ -148,15 +210,15 @@ function Spinner({ label }) {
|
|
|
148
210
|
return () => clearInterval(id);
|
|
149
211
|
}, []);
|
|
150
212
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
151
|
-
/* @__PURE__ */ jsxs(Text, { color:
|
|
213
|
+
/* @__PURE__ */ jsxs(Text, { color: theme.accent, children: [
|
|
152
214
|
frames[i],
|
|
153
215
|
" "
|
|
154
216
|
] }),
|
|
155
|
-
/* @__PURE__ */
|
|
217
|
+
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: label })
|
|
156
218
|
] });
|
|
157
219
|
}
|
|
158
220
|
var TabBar = memo(function TabBar2({ tabs, active, onSelect }) {
|
|
159
|
-
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: [
|
|
160
222
|
" ",
|
|
161
223
|
t,
|
|
162
224
|
" "
|
|
@@ -167,14 +229,15 @@ var TabBar = memo(function TabBar2({ tabs, active, onSelect }) {
|
|
|
167
229
|
] }) }, t)) });
|
|
168
230
|
});
|
|
169
231
|
var PeakBadge = memo(function PeakBadge2({ peak, resetDisplay = "relative", tz }) {
|
|
170
|
-
const
|
|
232
|
+
const theme = useTuiTheme();
|
|
233
|
+
const color = peak.state === "peak" ? theme.crit : theme.ok;
|
|
171
234
|
const changesAt = peak.changesAt ?? (peak.minutesUntilChange !== null ? new Date(Date.now() + peak.minutesUntilChange * 6e4).toISOString() : null);
|
|
172
235
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
173
236
|
/* @__PURE__ */ jsxs(Text, { color, children: [
|
|
174
237
|
glyphs().dot,
|
|
175
238
|
" "
|
|
176
239
|
] }),
|
|
177
|
-
/* @__PURE__ */
|
|
240
|
+
/* @__PURE__ */ jsx2(Text, { bold: true, color, children: peak.label }),
|
|
178
241
|
changesAt && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
179
242
|
" (",
|
|
180
243
|
formatResetAt(changesAt, resetDisplay, Date.now(), tz),
|
|
@@ -199,8 +262,8 @@ function sparkline(values) {
|
|
|
199
262
|
function Bar({ pct, color, width = 24 }) {
|
|
200
263
|
const filled = Math.max(0, Math.min(width, Math.round(pct / 100 * width)));
|
|
201
264
|
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
202
|
-
/* @__PURE__ */
|
|
203
|
-
/* @__PURE__ */
|
|
265
|
+
/* @__PURE__ */ jsx2(Text, { color, children: glyphs().barFull.repeat(filled) }),
|
|
266
|
+
/* @__PURE__ */ jsx2(Text, { dimColor: true, children: glyphs().barEmpty.repeat(width - filled) })
|
|
204
267
|
] });
|
|
205
268
|
}
|
|
206
269
|
function metricValueText(m) {
|
|
@@ -216,6 +279,20 @@ function metricValueText(m) {
|
|
|
216
279
|
}
|
|
217
280
|
return `${Math.round(m.used)}%`;
|
|
218
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
|
+
}
|
|
219
296
|
|
|
220
297
|
// src/ui/dashboard.tsx
|
|
221
298
|
import { memo as memo2 } from "react";
|
|
@@ -244,8 +321,58 @@ function planDisplay(rawPlans) {
|
|
|
244
321
|
return { mode: "perRow", count: plans.length };
|
|
245
322
|
}
|
|
246
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
|
+
|
|
247
370
|
// src/ui/dashboard.tsx
|
|
248
|
-
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
|
+
}
|
|
249
376
|
var GAP = 2;
|
|
250
377
|
var MIN_CARD = 56;
|
|
251
378
|
var MIN_CARD_DENSE = 50;
|
|
@@ -263,8 +390,8 @@ function estimateCardHeights(groups, stats) {
|
|
|
263
390
|
const multi = g.accounts.length > 1;
|
|
264
391
|
g.accounts.forEach((a, i) => {
|
|
265
392
|
const s = stats.get(a.id);
|
|
266
|
-
const staleRow = s?.billing && !s.billing.error && s.
|
|
267
|
-
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;
|
|
268
395
|
h += metricRows + (multi ? 1 : 0) + (multi && i > 0 ? 1 : 0);
|
|
269
396
|
});
|
|
270
397
|
}
|
|
@@ -319,7 +446,7 @@ function computeDashLayout(groups, stats, cols, budget, focusId, layoutPref) {
|
|
|
319
446
|
}
|
|
320
447
|
return chooseLayout(content, budget, groups.length, single, cols, heights);
|
|
321
448
|
}
|
|
322
|
-
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 }) {
|
|
323
450
|
if (groups.length === 0) {
|
|
324
451
|
return /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
325
452
|
"No providers enabled ",
|
|
@@ -328,13 +455,13 @@ var DashboardView = memo2(function DashboardView2({ groups, stats, cols, budget,
|
|
|
328
455
|
] });
|
|
329
456
|
}
|
|
330
457
|
const content = Math.max(30, cols - 4);
|
|
331
|
-
const { ncols, variant, cardsPerPage, pageCount } =
|
|
458
|
+
const { ncols, variant, cardsPerPage, pageCount } = computed;
|
|
332
459
|
let cardW = ncols <= 1 ? content : Math.floor((content - GAP * (ncols - 1)) / ncols);
|
|
333
460
|
if (ncols === 1 && cardW > MAX_SINGLE_CARD) cardW = MAX_SINGLE_CARD;
|
|
334
461
|
const pg = pageCount > 1 ? (page % pageCount + pageCount) % pageCount : 0;
|
|
335
462
|
const visible = pageCount > 1 ? groups.slice(pg * cardsPerPage, pg * cardsPerPage + cardsPerPage) : groups;
|
|
336
463
|
return /* @__PURE__ */ jsxs2(Box2, { height: budget, flexDirection: "column", overflow: "hidden", children: [
|
|
337
|
-
/* @__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)) }),
|
|
338
465
|
pageCount > 1 && /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
339
466
|
" ",
|
|
340
467
|
glyphs().middot,
|
|
@@ -351,127 +478,143 @@ var DashboardView = memo2(function DashboardView2({ groups, stats, cols, budget,
|
|
|
351
478
|
] });
|
|
352
479
|
});
|
|
353
480
|
function ProviderCard({ provider, accounts, stats, width, variant, privacyMode = false, resetDisplay, tz }) {
|
|
481
|
+
const theme = useTuiTheme();
|
|
354
482
|
const meta = PROVIDERS[provider];
|
|
355
483
|
const items = accounts.map((a) => ({ account: a, s: stats.get(a.id) }));
|
|
356
|
-
const
|
|
357
|
-
const agg = meta.hasUsage && dashboards.length > 0 ? aggregate(dashboards) : null;
|
|
484
|
+
const agg = meta.hasUsage ? aggregateDashboardData(items.map((i) => i.s?.dashboard)) : null;
|
|
358
485
|
const planView = planDisplay(items.map((i) => i.s?.billing?.plan));
|
|
359
486
|
const activity = items.map((i) => i.s?.billing?.activity).find(Boolean) ?? null;
|
|
360
487
|
const inner = width - 4;
|
|
361
488
|
const hasSpark = !!agg && agg.series.some((v) => v > 0);
|
|
362
489
|
const showBars = variant !== "mini";
|
|
363
490
|
const showSpark = variant === "full";
|
|
491
|
+
const headroom = items.find((item) => item.s?.providerHeadroom)?.s?.providerHeadroom;
|
|
364
492
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width, borderStyle: glyphs().border, borderColor: meta.color, paddingX: 1, children: [
|
|
365
493
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
366
494
|
/* @__PURE__ */ jsxs2(Text2, { color: meta.color, children: [
|
|
367
495
|
glyphs().dot,
|
|
368
496
|
" "
|
|
369
497
|
] }),
|
|
370
|
-
/* @__PURE__ */
|
|
371
|
-
/* @__PURE__ */
|
|
372
|
-
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 }),
|
|
373
501
|
planView.mode === "perRow" && /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
374
502
|
planView.count,
|
|
375
503
|
" accounts"
|
|
376
504
|
] })
|
|
377
505
|
] }),
|
|
378
|
-
|
|
379
|
-
/* @__PURE__ */
|
|
380
|
-
/* @__PURE__ */
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
] })
|
|
385
|
-
|
|
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 }),
|
|
386
521
|
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
387
522
|
"Fetching usage",
|
|
388
523
|
glyphs().ellipsis
|
|
389
524
|
] })
|
|
390
525
|
] })),
|
|
391
|
-
meta.hasBilling && showBars && /* @__PURE__ */ jsxs2(
|
|
392
|
-
meta.hasUsage && /* @__PURE__ */
|
|
393
|
-
/* @__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 })
|
|
394
529
|
] }),
|
|
395
|
-
meta.hasBilling && !showBars && !meta.hasUsage && /* @__PURE__ */
|
|
396
|
-
hasSpark && showSpark && /* @__PURE__ */ jsxs2(
|
|
397
|
-
/* @__PURE__ */
|
|
398
|
-
/* @__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 })
|
|
399
534
|
] }),
|
|
400
|
-
!meta.hasUsage && activity && showSpark && /* @__PURE__ */ jsxs2(
|
|
401
|
-
/* @__PURE__ */
|
|
535
|
+
!meta.hasUsage && activity && showSpark && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
536
|
+
/* @__PURE__ */ jsx3(Rule, { inner }),
|
|
402
537
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
403
|
-
/* @__PURE__ */
|
|
404
|
-
/* @__PURE__ */
|
|
405
|
-
/* @__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 }) })
|
|
406
541
|
] })
|
|
407
542
|
] })
|
|
408
543
|
] });
|
|
409
544
|
}
|
|
410
545
|
function CompactBilling({ items, privacyMode }) {
|
|
411
|
-
const
|
|
546
|
+
const theme = useTuiTheme();
|
|
547
|
+
const stats = items.map((item) => item.s).find((value) => value?.billing);
|
|
548
|
+
const billing = stats?.billing;
|
|
412
549
|
if (!billing) return /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
413
550
|
"Fetching",
|
|
414
551
|
glyphs().ellipsis
|
|
415
552
|
] });
|
|
416
|
-
if (billing.error) return /* @__PURE__ */
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
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 });
|
|
420
558
|
}
|
|
421
559
|
function Rule({ inner }) {
|
|
422
|
-
return /* @__PURE__ */
|
|
560
|
+
return /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: glyphs().rule.repeat(Math.max(0, inner)) });
|
|
423
561
|
}
|
|
424
562
|
function SummaryRow({ label, s }) {
|
|
425
|
-
const
|
|
563
|
+
const theme = useTuiTheme();
|
|
564
|
+
const cachedPct = cachedTokenPercentage(s);
|
|
426
565
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
427
|
-
/* @__PURE__ */
|
|
428
|
-
/* @__PURE__ */
|
|
429
|
-
/* @__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: [
|
|
430
569
|
tokens(s.tokens),
|
|
431
570
|
" tok"
|
|
432
571
|
] }) }),
|
|
433
|
-
/* @__PURE__ */
|
|
572
|
+
/* @__PURE__ */ jsx3(Box2, { flexGrow: 1, justifyContent: "flex-end", children: cachedPct > 0 ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, wrap: "truncate", children: [
|
|
434
573
|
cachedPct,
|
|
435
574
|
"% cached"
|
|
436
|
-
] }) : /* @__PURE__ */
|
|
575
|
+
] }) : /* @__PURE__ */ jsx3(Text2, { children: " " }) })
|
|
437
576
|
] });
|
|
438
577
|
}
|
|
439
578
|
function KpiLine({ agg }) {
|
|
579
|
+
const theme = useTuiTheme();
|
|
440
580
|
const hasBurn = agg.burnRate > 0;
|
|
441
581
|
const hasSaved = agg.month.cacheSavings > 0;
|
|
442
582
|
if (!hasBurn && !hasSaved) return null;
|
|
443
583
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
444
|
-
hasBurn && /* @__PURE__ */ jsxs2(
|
|
445
|
-
/* @__PURE__ */
|
|
446
|
-
/* @__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: [
|
|
447
587
|
currency(agg.burnRate),
|
|
448
588
|
"/hr"
|
|
449
589
|
] })
|
|
450
590
|
] }),
|
|
451
|
-
/* @__PURE__ */
|
|
452
|
-
hasSaved && /* @__PURE__ */ jsxs2(
|
|
453
|
-
/* @__PURE__ */
|
|
454
|
-
/* @__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: [
|
|
455
595
|
currency(agg.month.cacheSavings),
|
|
456
596
|
"/mo"
|
|
457
597
|
] })
|
|
458
598
|
] })
|
|
459
599
|
] });
|
|
460
600
|
}
|
|
461
|
-
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);
|
|
462
603
|
const email = billing?.email;
|
|
463
604
|
const title = email && !account.name.includes("@") ? `${account.name} ${email}` : account.name;
|
|
464
605
|
return privacyMode ? redactEmail(title) : title;
|
|
465
606
|
}
|
|
466
|
-
function LimitsBlock({ items, inner, showRowPlans, privacyMode, resetDisplay, tz }) {
|
|
607
|
+
function LimitsBlock({ items, inner, showRowPlans, privacyMode, resetDisplay, tz, providerName }) {
|
|
608
|
+
const theme = useTuiTheme();
|
|
467
609
|
const showName = items.length > 1;
|
|
468
|
-
const labels = items.flatMap((i) => i.s
|
|
610
|
+
const labels = items.flatMap((i) => quotaViews(i.s)).map((quota) => quota.label.length);
|
|
469
611
|
const labelW = Math.min(Math.max(7, ...labels) + 1, 14);
|
|
470
612
|
const resetW = resetDisplay === "absolute" ? 17 : 8;
|
|
471
613
|
const barW = Math.max(10, Math.min(46, inner - labelW - resetW - 5));
|
|
472
|
-
return /* @__PURE__ */
|
|
614
|
+
return /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", children: items.map(({ account, s }, idx) => {
|
|
473
615
|
const billing = s?.billing;
|
|
474
|
-
const
|
|
616
|
+
const quotas = quotaViews(s);
|
|
617
|
+
const staleLabel = billing && !billing.error && quotas.length > 0 ? billingStaleLabel(billing.asOfMs ?? s?.billingUpdatedAt, Date.now()) : null;
|
|
475
618
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: showName && idx > 0 ? 1 : 0, children: [
|
|
476
619
|
showName && (() => {
|
|
477
620
|
const rowPlan = showRowPlans ? normalizePlan(billing?.plan) : null;
|
|
@@ -484,18 +627,18 @@ function LimitsBlock({ items, inner, showRowPlans, privacyMode, resetDisplay, tz
|
|
|
484
627
|
glyphs().dot,
|
|
485
628
|
" "
|
|
486
629
|
] }),
|
|
487
|
-
/* @__PURE__ */
|
|
488
|
-
shownPlan && /* @__PURE__ */ jsxs2(
|
|
489
|
-
/* @__PURE__ */
|
|
490
|
-
/* @__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 })
|
|
491
634
|
] })
|
|
492
635
|
] });
|
|
493
636
|
})(),
|
|
494
637
|
!billing ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
495
638
|
"Fetching",
|
|
496
639
|
glyphs().ellipsis
|
|
497
|
-
] }) : billing.error ? /* @__PURE__ */
|
|
498
|
-
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: [
|
|
499
642
|
glyphs().warn,
|
|
500
643
|
" ",
|
|
501
644
|
staleLabel,
|
|
@@ -507,71 +650,45 @@ function LimitsBlock({ items, inner, showRowPlans, privacyMode, resetDisplay, tz
|
|
|
507
650
|
] }, account.id);
|
|
508
651
|
}) });
|
|
509
652
|
}
|
|
510
|
-
function
|
|
511
|
-
|
|
512
|
-
|
|
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);
|
|
513
662
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
514
|
-
/* @__PURE__ */
|
|
515
|
-
/* @__PURE__ */
|
|
516
|
-
/* @__PURE__ */
|
|
517
|
-
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),
|
|
518
667
|
"%"
|
|
519
668
|
] }) }),
|
|
520
|
-
/* @__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: " " }) })
|
|
521
670
|
] });
|
|
522
671
|
}
|
|
523
672
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
524
|
-
/* @__PURE__ */
|
|
525
|
-
/* @__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 })
|
|
526
675
|
] });
|
|
527
676
|
}
|
|
528
677
|
function SparkFooter({ series, month, color }) {
|
|
529
678
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
530
|
-
/* @__PURE__ */
|
|
531
|
-
/* @__PURE__ */
|
|
532
|
-
/* @__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: [
|
|
533
682
|
currency(month),
|
|
534
683
|
" mo"
|
|
535
684
|
] }) })
|
|
536
685
|
] });
|
|
537
686
|
}
|
|
538
|
-
function aggregate(list) {
|
|
539
|
-
const zero = () => ({ cost: 0, tokens: 0, input: 0, cacheRead: 0, cacheSavings: 0 });
|
|
540
|
-
const z = { today: zero(), week: zero(), month: zero(), burnRate: 0, series: [] };
|
|
541
|
-
for (const d of list) {
|
|
542
|
-
for (const k of ["today", "week", "month"]) {
|
|
543
|
-
z[k].cost += d[k].cost;
|
|
544
|
-
z[k].tokens += d[k].tokens;
|
|
545
|
-
z[k].input += d[k].input ?? 0;
|
|
546
|
-
z[k].cacheRead += d[k].cacheRead;
|
|
547
|
-
z[k].cacheSavings += d[k].cacheSavings;
|
|
548
|
-
}
|
|
549
|
-
z.burnRate += d.burnRate;
|
|
550
|
-
d.series.forEach((v, i) => {
|
|
551
|
-
z.series[i] = (z.series[i] ?? 0) + v;
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
return z;
|
|
555
|
-
}
|
|
556
687
|
var TotalsRow = memo2(function TotalsRow2({ groups, stats, cols }) {
|
|
557
|
-
const
|
|
558
|
-
const t =
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
for (const a of g.accounts) {
|
|
562
|
-
const d = stats.get(a.id)?.dashboard;
|
|
563
|
-
if (!d) continue;
|
|
564
|
-
t.cost += d.today.cost;
|
|
565
|
-
t.tokens += d.today.tokens;
|
|
566
|
-
t.input += d.today.input ?? 0;
|
|
567
|
-
w.cost += d.week.cost;
|
|
568
|
-
w.tokens += d.week.tokens;
|
|
569
|
-
w.input += d.week.input ?? 0;
|
|
570
|
-
m.cost += d.month.cost;
|
|
571
|
-
m.tokens += d.month.tokens;
|
|
572
|
-
m.input += d.month.input ?? 0;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
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 };
|
|
575
692
|
const inner = cols - 4;
|
|
576
693
|
const dot = glyphs().middot;
|
|
577
694
|
const monthNonCached = m.input > 0 ? `, ${tokens(m.input)} non-cached` : "";
|
|
@@ -580,920 +697,1219 @@ var TotalsRow = memo2(function TotalsRow2({ groups, stats, cols }) {
|
|
|
580
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)` : ""}`;
|
|
581
698
|
const tight = `${glyphs().dotAll} ${currency(t.cost)} ${dot} ${currency(w.cost)} ${dot} ${currency(m.cost)}`;
|
|
582
699
|
const text = full.length <= inner ? full : monthOnly.length <= inner ? monthOnly : noTok.length <= inner ? noTok : tight;
|
|
583
|
-
return /* @__PURE__ */
|
|
700
|
+
return /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: text }) });
|
|
584
701
|
});
|
|
585
702
|
|
|
586
703
|
// src/ui/table.tsx
|
|
587
|
-
import { memo as memo4 } from "react";
|
|
588
|
-
import { Box as Box4, Text as Text4 } from "ink";
|
|
589
|
-
|
|
590
|
-
// src/ui/settings.tsx
|
|
591
704
|
import { memo as memo3 } from "react";
|
|
592
705
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
593
|
-
import { Fragment as
|
|
594
|
-
var
|
|
595
|
-
var
|
|
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
|
-
|
|
624
|
-
/* @__PURE__ */
|
|
625
|
-
/* @__PURE__ */
|
|
626
|
-
/* @__PURE__ */
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
/* @__PURE__ */
|
|
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
|
-
|
|
677
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
678
|
-
" ",
|
|
679
|
-
glyphs().caretR
|
|
680
|
-
] })
|
|
681
|
-
] }),
|
|
682
|
-
/* @__PURE__ */ jsxs3(Row, { cursor, idx: 7, label: "Default focus", children: [
|
|
683
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
684
|
-
glyphs().caretL,
|
|
685
|
-
" "
|
|
686
|
-
] }),
|
|
687
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "yellow", children: config.defaultFocus === "all" ? "All" : "Last account" }),
|
|
688
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
689
|
-
" ",
|
|
690
|
-
glyphs().caretR
|
|
691
|
-
] })
|
|
692
|
-
] }),
|
|
693
|
-
/* @__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" }) }),
|
|
694
|
-
/* @__PURE__ */ jsx3(Row, { cursor, idx: 9, label: "Allowed hosts", children: editingAllowedHosts ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
695
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "[" }),
|
|
696
|
-
/* @__PURE__ */ jsx3(CaretText, { value: allowedHostsEdit ?? "", caret: allowedHostsCaret, color: "cyan" }),
|
|
697
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "]" })
|
|
698
|
-
] }) : /* @__PURE__ */ jsx3(Text3, { bold: true, color: "yellow", children: config.allowedHosts.join(", ") || "none" }) }),
|
|
699
|
-
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: [
|
|
700
790
|
" ",
|
|
701
|
-
|
|
791
|
+
col("Date", W.label, "left")
|
|
702
792
|
] }),
|
|
703
|
-
|
|
704
|
-
/* @__PURE__ */
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
711
|
-
" ",
|
|
712
|
-
glyphs().caretR
|
|
713
|
-
] })
|
|
714
|
-
] })
|
|
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) })
|
|
715
800
|
] }),
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
" "
|
|
726
|
-
] }),
|
|
727
|
-
/* @__PURE__ */ jsx3(Text3, { bold: enabled, color: enabled ? p.color : void 0, dimColor: !enabled, children: enabled ? `[${glyphs().check}]` : "[ ]" }),
|
|
728
|
-
/* @__PURE__ */ jsxs3(Text3, { color: p.color, children: [
|
|
729
|
-
" ",
|
|
730
|
-
glyphs().dot,
|
|
731
|
-
" "
|
|
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")
|
|
732
810
|
] }),
|
|
733
|
-
/* @__PURE__ */
|
|
734
|
-
/* @__PURE__ */
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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")
|
|
744
827
|
] }),
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
const plan = identity?.plan ?? null;
|
|
753
|
-
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
754
|
-
/* @__PURE__ */ jsxs3(Text3, { color: selected ? "green" : void 0, children: [
|
|
755
|
-
selected ? glyphs().caretR : " ",
|
|
756
|
-
" "
|
|
757
|
-
] }),
|
|
758
|
-
/* @__PURE__ */ jsxs3(Text3, { color: acc.color || provider.color, children: [
|
|
759
|
-
isActive ? glyphs().dot : glyphs().radioOff,
|
|
760
|
-
" "
|
|
761
|
-
] }),
|
|
762
|
-
/* @__PURE__ */ jsx3(Box3, { width: 28, children: /* @__PURE__ */ jsx3(Text3, { bold: true, children: truncateName(identityLabel, 27) }) }),
|
|
763
|
-
/* @__PURE__ */ jsx3(Box3, { width: 9, children: /* @__PURE__ */ jsx3(Text3, { color: provider.color, children: provider.name }) }),
|
|
764
|
-
/* @__PURE__ */ jsx3(Box3, { width: 18, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: plan ? truncateName(plan, 17) : "" }) }),
|
|
765
|
-
/* @__PURE__ */ jsx3(Box3, { width: 12, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: acc.source === "auto" ? "auto tracking" : "configured" }) }),
|
|
766
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: truncateName(acc.homeDir, 24) })
|
|
767
|
-
] }, `${acc.source}:${acc.id}`);
|
|
768
|
-
}),
|
|
769
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
770
|
-
/* @__PURE__ */ jsxs3(Text3, { color: cursor === trackedAccounts.length ? "green" : void 0, children: [
|
|
771
|
-
cursor === trackedAccounts.length ? glyphs().caretR : " ",
|
|
772
|
-
" "
|
|
773
|
-
] }),
|
|
774
|
-
/* @__PURE__ */ jsx3(Text3, { color: "greenBright", children: "+ " }),
|
|
775
|
-
/* @__PURE__ */ jsx3(Text3, { children: "Add account" })
|
|
776
|
-
] })
|
|
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) })
|
|
777
835
|
] }),
|
|
778
|
-
/* @__PURE__ */
|
|
779
|
-
|
|
780
|
-
glyphs().arrowL,
|
|
781
|
-
glyphs().arrowR,
|
|
782
|
-
"/tab switch section ",
|
|
783
|
-
glyphs().middot,
|
|
784
|
-
" ",
|
|
785
|
-
glyphs().arrowD,
|
|
786
|
-
" rows ",
|
|
787
|
-
glyphs().middot,
|
|
788
|
-
" s/Esc close"
|
|
789
|
-
] }) : editingTz ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
790
|
-
"type IANA name (e.g. Europe/London) ",
|
|
791
|
-
glyphs().middot,
|
|
792
|
-
" empty = System ",
|
|
793
|
-
glyphs().middot,
|
|
794
|
-
" Enter save ",
|
|
795
|
-
glyphs().middot,
|
|
796
|
-
" Esc cancel"
|
|
797
|
-
] }) : editingAllowedHosts ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
798
|
-
"comma-separated exact DNS names ",
|
|
799
|
-
glyphs().middot,
|
|
800
|
-
" Enter save ",
|
|
801
|
-
glyphs().middot,
|
|
802
|
-
" Esc cancel"
|
|
803
|
-
] }) : activeTab === "providers" ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
836
|
+
/* @__PURE__ */ jsx4(Box3, { height: 1 }),
|
|
837
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
804
838
|
glyphs().arrowU,
|
|
805
839
|
glyphs().arrowD,
|
|
806
|
-
"
|
|
807
|
-
glyphs().middot,
|
|
808
|
-
" space toggle provider ",
|
|
809
|
-
glyphs().middot,
|
|
810
|
-
" s/Esc close"
|
|
811
|
-
] }) : activeTab === "accounts" && cursor >= 0 && cursor < trackedAccounts.length ? trackedAccounts[cursor]?.source === "auto" ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
812
|
-
glyphs().arrowU,
|
|
813
|
-
glyphs().arrowD,
|
|
814
|
-
" select ",
|
|
840
|
+
" navigate ",
|
|
815
841
|
glyphs().middot,
|
|
816
|
-
" Enter
|
|
842
|
+
" Enter detail ",
|
|
817
843
|
glyphs().middot,
|
|
818
|
-
"
|
|
844
|
+
" o sort ",
|
|
819
845
|
glyphs().middot,
|
|
820
|
-
"
|
|
821
|
-
] }) : /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
822
|
-
glyphs().arrowU,
|
|
823
|
-
glyphs().arrowD,
|
|
824
|
-
" select ",
|
|
846
|
+
" g/G top/bottom ",
|
|
825
847
|
glyphs().middot,
|
|
826
848
|
" ",
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
" reorder ",
|
|
831
|
-
glyphs().middot,
|
|
832
|
-
" Enter edit ",
|
|
833
|
-
glyphs().middot,
|
|
834
|
-
" space activate ",
|
|
835
|
-
glyphs().middot,
|
|
836
|
-
" d delete ",
|
|
837
|
-
glyphs().middot,
|
|
838
|
-
" s/Esc close"
|
|
839
|
-
] }) : activeTab === "accounts" && cursor === trackedAccounts.length ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
840
|
-
glyphs().arrowU,
|
|
841
|
-
glyphs().arrowD,
|
|
842
|
-
" select ",
|
|
843
|
-
glyphs().middot,
|
|
844
|
-
" Enter add account ",
|
|
845
|
-
glyphs().middot,
|
|
846
|
-
" s/Esc close"
|
|
847
|
-
] }) : /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
848
|
-
glyphs().arrowU,
|
|
849
|
-
glyphs().arrowD,
|
|
850
|
-
" select ",
|
|
851
|
-
glyphs().arrowL,
|
|
852
|
-
glyphs().arrowR,
|
|
853
|
-
" adjust Enter edit tab switch section s/Esc close"
|
|
849
|
+
clampedCursor + 1,
|
|
850
|
+
"/",
|
|
851
|
+
rows.length
|
|
854
852
|
] })
|
|
855
853
|
] });
|
|
856
854
|
});
|
|
857
|
-
function
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
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,
|
|
861
860
|
" "
|
|
862
861
|
] }),
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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 : " ",
|
|
869
906
|
" "
|
|
870
|
-
] })
|
|
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: [
|
|
871
910
|
" ",
|
|
872
|
-
|
|
911
|
+
glyphs().dot,
|
|
873
912
|
" "
|
|
874
913
|
] }),
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
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
|
+
] })
|
|
878
938
|
] });
|
|
879
939
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
] });
|
|
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;
|
|
893
952
|
}
|
|
894
|
-
function
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
children
|
|
902
|
-
] });
|
|
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
|
+
};
|
|
903
960
|
}
|
|
904
|
-
function
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
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
|
|
915
1001
|
] }),
|
|
916
|
-
/* @__PURE__ */
|
|
917
|
-
"
|
|
918
|
-
|
|
919
|
-
"
|
|
1002
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1003
|
+
" ",
|
|
1004
|
+
groups.length,
|
|
1005
|
+
" found"
|
|
920
1006
|
] })
|
|
921
1007
|
] }),
|
|
922
|
-
/* @__PURE__ */
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
label: "Home directory",
|
|
943
|
-
hint: `path containing the tool's data dir ${glyphs().middot} ~ for default`,
|
|
944
|
-
value: form.homeDir,
|
|
945
|
-
focused: form.field === "homeDir",
|
|
946
|
-
caret: form.caret,
|
|
947
|
-
accent,
|
|
948
|
-
placeholder: "~/work",
|
|
949
|
-
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);
|
|
950
1028
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
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"
|
|
967
1057
|
] })
|
|
968
1058
|
] }),
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
976
|
-
"tab/",
|
|
977
|
-
glyphs().arrowU,
|
|
978
|
-
glyphs().arrowD,
|
|
979
|
-
" "
|
|
980
|
-
] }),
|
|
981
|
-
/* @__PURE__ */ jsx3(Text3, { children: "switch field" }),
|
|
982
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
983
|
-
" ",
|
|
984
|
-
glyphs().middot,
|
|
985
|
-
" "
|
|
986
|
-
] }),
|
|
987
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "enter " }),
|
|
988
|
-
/* @__PURE__ */ jsx3(Text3, { children: form.field === "color" ? "save" : "next" }),
|
|
989
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
990
|
-
" ",
|
|
991
|
-
glyphs().middot,
|
|
992
|
-
" "
|
|
993
|
-
] }),
|
|
994
|
-
form.field === "color" || form.field === "provider" ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
995
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
996
|
-
glyphs().arrowL,
|
|
997
|
-
glyphs().arrowR,
|
|
998
|
-
" "
|
|
999
|
-
] }),
|
|
1000
|
-
/* @__PURE__ */ jsx3(Text3, { children: form.field === "provider" ? "pick provider" : "pick color" }),
|
|
1001
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1002
|
-
" ",
|
|
1003
|
-
glyphs().middot,
|
|
1004
|
-
" "
|
|
1005
|
-
] })
|
|
1006
|
-
] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1007
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1008
|
-
glyphs().arrowL,
|
|
1009
|
-
glyphs().arrowR,
|
|
1010
|
-
" "
|
|
1011
|
-
] }),
|
|
1012
|
-
/* @__PURE__ */ jsx3(Text3, { children: "move caret" }),
|
|
1013
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1014
|
-
" ",
|
|
1015
|
-
glyphs().middot,
|
|
1016
|
-
" "
|
|
1017
|
-
] })
|
|
1059
|
+
/* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
1060
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1061
|
+
"loading ",
|
|
1062
|
+
readyCount,
|
|
1063
|
+
" / ",
|
|
1064
|
+
groups.length
|
|
1018
1065
|
] }),
|
|
1019
|
-
/* @__PURE__ */
|
|
1020
|
-
/* @__PURE__ */ jsx3(Text3, { children: "save" }),
|
|
1021
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1066
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1022
1067
|
" ",
|
|
1023
1068
|
glyphs().middot,
|
|
1024
|
-
" "
|
|
1025
|
-
] })
|
|
1026
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "esc " }),
|
|
1027
|
-
/* @__PURE__ */ jsx3(Text3, { children: "cancel" })
|
|
1069
|
+
" W opens the web dashboard once loaded"
|
|
1070
|
+
] })
|
|
1028
1071
|
] })
|
|
1029
1072
|
] });
|
|
1030
1073
|
}
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
" "
|
|
1053
|
-
] })
|
|
1054
|
-
] }, s.id);
|
|
1055
|
-
}) });
|
|
1056
|
-
}
|
|
1057
|
-
function ProviderField({ value, focused }) {
|
|
1058
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
1059
|
-
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: focused ? PROVIDERS[value].color : void 0, bold: focused, dimColor: !focused, children: [
|
|
1060
|
-
focused ? glyphs().caretR : " ",
|
|
1061
|
-
" Provider"
|
|
1062
|
-
] }) }),
|
|
1063
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
1064
|
-
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
1065
|
-
" ",
|
|
1066
|
-
focused ? glyphs().vbar : " ",
|
|
1067
|
-
" "
|
|
1068
|
-
] }),
|
|
1069
|
-
PROVIDER_ORDER.map((pid) => {
|
|
1070
|
-
const selected = pid === value;
|
|
1071
|
-
const p = PROVIDERS[pid];
|
|
1072
|
-
return /* @__PURE__ */ jsx3(Box3, { marginRight: 2, children: selected ? /* @__PURE__ */ jsxs3(Text3, { bold: true, color: p.color, children: [
|
|
1073
|
-
"[",
|
|
1074
|
-
p.name,
|
|
1075
|
-
"]"
|
|
1076
|
-
] }) : /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: p.name }) }, pid);
|
|
1077
|
-
})
|
|
1078
|
-
] }),
|
|
1079
|
-
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " which tool this account tracks" }) })
|
|
1080
|
-
] });
|
|
1081
|
-
}
|
|
1082
|
-
function FormField({ label, hint, value, focused, caret, accent, placeholder, mono }) {
|
|
1083
|
-
const isPlaceholder = value === "";
|
|
1084
|
-
const display = isPlaceholder ? placeholder : value;
|
|
1085
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
1086
|
-
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: focused ? accent : void 0, bold: focused, dimColor: !focused, children: [
|
|
1087
|
-
focused ? glyphs().caretR : " ",
|
|
1088
|
-
" ",
|
|
1089
|
-
label
|
|
1090
|
-
] }) }),
|
|
1091
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
1092
|
-
/* @__PURE__ */ jsxs3(Text3, { color: focused ? accent : void 0, children: [
|
|
1093
|
-
" ",
|
|
1094
|
-
focused ? glyphs().vbar : " ",
|
|
1095
|
-
" "
|
|
1096
|
-
] }),
|
|
1097
|
-
focused ? isPlaceholder ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1098
|
-
/* @__PURE__ */ jsx3(Text3, { color: accent, children: glyphs().vbar }),
|
|
1099
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, italic: mono, children: placeholder })
|
|
1100
|
-
] }) : /* @__PURE__ */ jsx3(CaretText, { value, caret: caret ?? value.length, color: accent }) : /* @__PURE__ */ jsx3(Text3, { dimColor: isPlaceholder, italic: mono && isPlaceholder, children: display })
|
|
1101
|
-
] }),
|
|
1102
|
-
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1103
|
-
" ",
|
|
1104
|
-
hint
|
|
1105
|
-
] }) })
|
|
1106
|
-
] });
|
|
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];
|
|
1107
1095
|
}
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
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: [
|
|
1116
1210
|
" ",
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
COLOR_PALETTE.map((c) => /* @__PURE__ */ jsx3(Box3, { marginRight: 1, children: c === value ? /* @__PURE__ */ jsxs3(Text3, { bold: true, color: c, children: [
|
|
1121
|
-
"[",
|
|
1122
|
-
glyphs().dot,
|
|
1123
|
-
"]"
|
|
1124
|
-
] }) : /* @__PURE__ */ jsxs3(Text3, { color: c, dimColor: !focused, children: [
|
|
1125
|
-
" ",
|
|
1126
|
-
glyphs().dot
|
|
1127
|
-
] }) }, 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
|
|
1128
1214
|
] }),
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
/* @__PURE__ */
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
/* @__PURE__ */
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
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))
|
|
1170
1407
|
] }),
|
|
1171
|
-
|
|
1172
|
-
/* @__PURE__ */
|
|
1173
|
-
/* @__PURE__ */
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
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." })
|
|
1178
1418
|
] })
|
|
1179
1419
|
] }),
|
|
1180
|
-
|
|
1181
|
-
/* @__PURE__ */
|
|
1182
|
-
/* @__PURE__ */
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
/* @__PURE__ */
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
" esc clear)"
|
|
1190
|
-
] })
|
|
1191
|
-
] }) : /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "/ filter" })
|
|
1192
|
-
] });
|
|
1193
|
-
});
|
|
1194
|
-
var TokenTable = memo4(function TokenTable2({ rows, cursor, expanded, maxRows, cols, onRowClick }) {
|
|
1195
|
-
if (rows.length === 0) return /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No usage in this period (or filtered out)." });
|
|
1196
|
-
const wide = cols > 90;
|
|
1197
|
-
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 };
|
|
1198
|
-
const fixed = base.label + base.input + base.output + base.cc + base.cr + base.total + base.cost;
|
|
1199
|
-
const available = cols - fixed - 6;
|
|
1200
|
-
const W = { ...base, models: Math.max(wide ? 22 : 14, available) };
|
|
1201
|
-
const lineW = W.label + W.models + W.input + W.output + W.cc + W.cr + W.total + W.cost;
|
|
1202
|
-
const totals = { input: 0, output: 0, cacheCreate: 0, cacheRead: 0, cost: 0 };
|
|
1203
|
-
for (const r of rows) {
|
|
1204
|
-
totals.input += r.input;
|
|
1205
|
-
totals.output += r.output;
|
|
1206
|
-
totals.cacheCreate += r.cacheCreate;
|
|
1207
|
-
totals.cacheRead += r.cacheRead;
|
|
1208
|
-
totals.cost += r.cost;
|
|
1209
|
-
}
|
|
1210
|
-
const clampedCursor = Math.min(cursor, rows.length - 1);
|
|
1211
|
-
const scrollStart = Math.max(0, Math.min(clampedCursor - Math.floor(maxRows / 2), rows.length - maxRows));
|
|
1212
|
-
const visible = rows.slice(scrollStart, scrollStart + maxRows);
|
|
1213
|
-
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
1214
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1215
|
-
/* @__PURE__ */ jsxs4(Text4, { bold: true, children: [
|
|
1216
|
-
" ",
|
|
1217
|
-
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" })
|
|
1218
1429
|
] }),
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
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
|
+
})
|
|
1226
1451
|
] }),
|
|
1227
|
-
/* @__PURE__ */
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
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
|
+
" "
|
|
1236
1471
|
] }),
|
|
1237
|
-
/* @__PURE__ */
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
/* @__PURE__ */
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
}),
|
|
1248
|
-
/* @__PURE__ */
|
|
1249
|
-
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
" ",
|
|
1252
|
-
|
|
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"
|
|
1253
1489
|
] }),
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
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
|
+
] })
|
|
1261
1524
|
] }),
|
|
1262
|
-
/* @__PURE__ */
|
|
1263
|
-
/* @__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: [
|
|
1264
1551
|
glyphs().arrowU,
|
|
1265
1552
|
glyphs().arrowD,
|
|
1266
|
-
"
|
|
1553
|
+
" select ",
|
|
1267
1554
|
glyphs().middot,
|
|
1268
|
-
"
|
|
1555
|
+
" ",
|
|
1556
|
+
glyphs().arrowL,
|
|
1557
|
+
glyphs().arrowR,
|
|
1558
|
+
" adjust ",
|
|
1269
1559
|
glyphs().middot,
|
|
1270
|
-
"
|
|
1560
|
+
" s/Esc close"
|
|
1561
|
+
] }) : activeTab === "desktop" ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1562
|
+
glyphs().arrowU,
|
|
1563
|
+
glyphs().arrowD,
|
|
1564
|
+
" select ",
|
|
1271
1565
|
glyphs().middot,
|
|
1272
|
-
"
|
|
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 ",
|
|
1273
1608
|
glyphs().middot,
|
|
1274
1609
|
" ",
|
|
1275
|
-
|
|
1276
|
-
"/",
|
|
1277
|
-
rows.length
|
|
1278
|
-
] })
|
|
1279
|
-
] });
|
|
1280
|
-
});
|
|
1281
|
-
function RowDetail({ row, indent }) {
|
|
1282
|
-
return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", paddingLeft: indent, children: row.breakdown.map((m, i) => /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1283
|
-
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
1284
|
-
i === row.breakdown.length - 1 ? glyphs().treeEnd : glyphs().treeMid,
|
|
1285
|
-
" "
|
|
1286
|
-
] }),
|
|
1287
|
-
/* @__PURE__ */ jsx4(Text4, { bold: true, children: col(m.name, 16, "left") }),
|
|
1288
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1289
|
-
col(tokens(m.input), 8),
|
|
1290
|
-
" in "
|
|
1291
|
-
] }),
|
|
1292
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1293
|
-
col(tokens(m.output), 8),
|
|
1294
|
-
" out "
|
|
1295
|
-
] }),
|
|
1296
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1297
|
-
col(tokens(m.cacheCreate), 8),
|
|
1298
|
-
" cc "
|
|
1299
|
-
] }),
|
|
1300
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1301
|
-
col(tokens(m.cacheRead), 9),
|
|
1302
|
-
" cr "
|
|
1303
|
-
] }),
|
|
1304
|
-
/* @__PURE__ */ jsx4(Text4, { bold: true, color: "yellow", children: currency(m.cost) })
|
|
1305
|
-
] }, m.name)) });
|
|
1306
|
-
}
|
|
1307
|
-
function fmtLabel(label) {
|
|
1308
|
-
if (label.length === 7 && label[4] === "-") {
|
|
1309
|
-
return `${MONTHS[Number(label.slice(5, 7))]} '${label.slice(2, 4)}`;
|
|
1310
|
-
}
|
|
1311
|
-
return shortDate(label);
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
// src/ui/onboarding.tsx
|
|
1315
|
-
import { Box as Box5, Text as Text5 } from "ink";
|
|
1316
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1317
|
-
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." }) {
|
|
1318
|
-
const anyEnabled = items.some((it) => it.enabled);
|
|
1319
|
-
const startIdx = items.length;
|
|
1320
|
-
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
1321
|
-
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text5, { bold: true, color: "greenBright", children: heading }) }),
|
|
1322
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: subheading }),
|
|
1323
|
-
/* @__PURE__ */ jsx5(Box5, { height: 1 }),
|
|
1324
|
-
items.map((it, i) => {
|
|
1325
|
-
const selected = cursor === i;
|
|
1326
|
-
const box = it.enabled ? `[${glyphs().check}]` : "[ ]";
|
|
1327
|
-
return /* @__PURE__ */ jsxs5(ClickableBox, { onClick: () => onToggle(i), children: [
|
|
1328
|
-
/* @__PURE__ */ jsxs5(Text5, { color: selected ? "green" : void 0, children: [
|
|
1329
|
-
selected ? glyphs().caretR : " ",
|
|
1330
|
-
" "
|
|
1331
|
-
] }),
|
|
1332
|
-
/* @__PURE__ */ jsx5(Text5, { bold: it.enabled, color: it.enabled ? it.color : void 0, dimColor: !it.enabled, children: box }),
|
|
1333
|
-
/* @__PURE__ */ jsxs5(Text5, { color: it.color, children: [
|
|
1334
|
-
" ",
|
|
1335
|
-
glyphs().dot,
|
|
1336
|
-
" "
|
|
1337
|
-
] }),
|
|
1338
|
-
/* @__PURE__ */ jsx5(Box5, { width: 13, flexShrink: 0, children: /* @__PURE__ */ jsx5(Text5, { bold: selected, dimColor: !it.detected && !it.enabled, wrap: "truncate", children: it.name }) }),
|
|
1339
|
-
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" })
|
|
1340
|
-
] }, it.id);
|
|
1341
|
-
}),
|
|
1342
|
-
/* @__PURE__ */ jsx5(Box5, { height: 1 }),
|
|
1343
|
-
/* @__PURE__ */ jsxs5(ClickableBox, { onClick: onConfirm, children: [
|
|
1344
|
-
/* @__PURE__ */ jsxs5(Text5, { color: cursor === startIdx ? "green" : void 0, children: [
|
|
1345
|
-
cursor === startIdx ? glyphs().caretR : " ",
|
|
1346
|
-
" "
|
|
1347
|
-
] }),
|
|
1348
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, color: anyEnabled ? "greenBright" : void 0, dimColor: !anyEnabled, children: anyEnabled ? `${glyphs().play} Start tokmon` : `${glyphs().play} Start (nothing selected)` })
|
|
1349
|
-
] }),
|
|
1350
|
-
/* @__PURE__ */ jsx5(Box5, { height: 1 }),
|
|
1351
|
-
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1610
|
+
glyphs().shift,
|
|
1352
1611
|
glyphs().arrowU,
|
|
1353
1612
|
glyphs().arrowD,
|
|
1354
|
-
"
|
|
1613
|
+
" reorder ",
|
|
1355
1614
|
glyphs().middot,
|
|
1356
|
-
"
|
|
1615
|
+
" Enter edit ",
|
|
1357
1616
|
glyphs().middot,
|
|
1358
|
-
"
|
|
1617
|
+
" space activate ",
|
|
1359
1618
|
glyphs().middot,
|
|
1360
|
-
"
|
|
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"
|
|
1361
1637
|
] })
|
|
1362
1638
|
] });
|
|
1639
|
+
});
|
|
1640
|
+
function themePresetLabel(preset) {
|
|
1641
|
+
return themePresetOption(preset).name;
|
|
1363
1642
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
1367
|
-
import { Box as Box6, Text as Text6 } from "ink";
|
|
1368
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1369
|
-
function accountReady(s, providerId) {
|
|
1370
|
-
if (!s) return false;
|
|
1371
|
-
const p = PROVIDERS[providerId];
|
|
1372
|
-
if (p.hasBilling && (s.billingState === "error" || s.billing?.error)) return true;
|
|
1373
|
-
if (p.hasUsage && s.summaryState !== "ready" && s.summaryState !== "error") return false;
|
|
1374
|
-
if (p.hasBilling && s.billingState !== "ready" && s.billingState !== "error") return false;
|
|
1375
|
-
return true;
|
|
1643
|
+
function appearanceModeLabel(mode) {
|
|
1644
|
+
return mode === "auto" ? "Auto (system)" : mode === "light" ? "Light" : "Dark";
|
|
1376
1645
|
}
|
|
1377
|
-
function
|
|
1378
|
-
|
|
1379
|
-
return {
|
|
1380
|
-
summaryState: s.dashboard ? "ready" : "pending",
|
|
1381
|
-
billingState: s.billing?.error ? "error" : s.billing ? "ready" : "pending",
|
|
1382
|
-
billing: s.billing
|
|
1383
|
-
};
|
|
1646
|
+
function terminalModeLabel(mode) {
|
|
1647
|
+
return mode === "ansi" ? "Terminal managed" : mode === "off" ? "No color" : mode === "light" ? "Light terminal" : "Dark terminal";
|
|
1384
1648
|
}
|
|
1385
|
-
function
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
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
|
+
] });
|
|
1400
1672
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
const id = setInterval(() => setFrame((f) => f + 1), 80);
|
|
1408
|
-
return () => clearInterval(id);
|
|
1409
|
-
}, []);
|
|
1410
|
-
const nameW = Math.min(13, groups.reduce((w, g) => Math.max(w, PROVIDERS[g.provider].name.length), 0));
|
|
1411
|
-
const readyCount = groups.filter((g) => g.accounts.every((a) => accountReady(resolveReady(a.id), g.provider))).length;
|
|
1412
|
-
const maxRows = Math.max(1, rows - 7);
|
|
1413
|
-
const visible = groups.slice(0, maxRows);
|
|
1414
|
-
const hidden = groups.length - visible.length;
|
|
1415
|
-
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1416
|
-
/* @__PURE__ */ jsxs6(Text6, { bold: true, color: "greenBright", children: [
|
|
1417
|
-
glyphs().dotSel,
|
|
1418
|
-
" 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
|
+
" "
|
|
1419
1679
|
] }),
|
|
1420
|
-
/* @__PURE__ */
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
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"
|
|
1424
1696
|
] }),
|
|
1425
1697
|
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1426
|
-
"
|
|
1427
|
-
|
|
1428
|
-
"
|
|
1698
|
+
" step ",
|
|
1699
|
+
step2,
|
|
1700
|
+
" of 4"
|
|
1429
1701
|
] })
|
|
1430
1702
|
] }),
|
|
1431
|
-
/* @__PURE__ */
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
] }),
|
|
1446
|
-
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1447
|
-
name,
|
|
1448
|
-
namePad
|
|
1449
|
-
] })
|
|
1450
|
-
] }, 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"
|
|
1451
1717
|
}
|
|
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
|
-
|
|
1479
|
-
|
|
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
|
+
] })
|
|
1480
1748
|
] })
|
|
1481
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
|
+
] }) }),
|
|
1482
1755
|
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, children: [
|
|
1483
1756
|
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1484
|
-
"
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1757
|
+
"tab/",
|
|
1758
|
+
glyphs().arrowU,
|
|
1759
|
+
glyphs().arrowD,
|
|
1760
|
+
" "
|
|
1488
1761
|
] }),
|
|
1762
|
+
/* @__PURE__ */ jsx7(Text6, { children: "switch field" }),
|
|
1489
1763
|
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1490
1764
|
" ",
|
|
1491
1765
|
glyphs().middot,
|
|
1492
|
-
"
|
|
1493
|
-
] })
|
|
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" })
|
|
1494
1809
|
] })
|
|
1495
1810
|
] });
|
|
1496
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
|
+
}
|
|
1497
1913
|
|
|
1498
1914
|
// src/ui/app-layout.logic.ts
|
|
1499
1915
|
function deriveSlots(accounts, privacyMode = false) {
|
|
@@ -1519,32 +1935,33 @@ function computeChrome(slots, cols, rows) {
|
|
|
1519
1935
|
|
|
1520
1936
|
// src/ui/resizing.tsx
|
|
1521
1937
|
import { Box as Box7, Text as Text7 } from "ink";
|
|
1522
|
-
import { jsx as
|
|
1938
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1523
1939
|
function ResizingView({ cols, rows }) {
|
|
1524
|
-
|
|
1940
|
+
const theme = useTuiTheme();
|
|
1941
|
+
return /* @__PURE__ */ jsx8(Box7, { width: cols, height: rows, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
1525
1942
|
glyphs().dotSel,
|
|
1526
1943
|
" resizing\u2026 ",
|
|
1527
|
-
/* @__PURE__ */
|
|
1944
|
+
/* @__PURE__ */ jsx8(Text7, { color: theme.accent, children: cols }),
|
|
1528
1945
|
"\xD7",
|
|
1529
|
-
/* @__PURE__ */
|
|
1946
|
+
/* @__PURE__ */ jsx8(Text7, { color: theme.accent, children: rows })
|
|
1530
1947
|
] }) });
|
|
1531
1948
|
}
|
|
1532
1949
|
|
|
1533
1950
|
// src/ui/account-strip.tsx
|
|
1534
1951
|
import { memo as memo5 } from "react";
|
|
1535
1952
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
1536
|
-
import { jsx as
|
|
1953
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1537
1954
|
var AccountStrip = memo5(function AccountStrip2({ slots, activeIdx, onSelect }) {
|
|
1538
|
-
return /* @__PURE__ */
|
|
1955
|
+
return /* @__PURE__ */ jsx9(Box8, { flexWrap: "wrap", children: slots.map((s, i) => {
|
|
1539
1956
|
const active = i === activeIdx;
|
|
1540
1957
|
const dot = s.id === null ? glyphs().dotAll : glyphs().dot;
|
|
1541
1958
|
const label = truncateName(s.name, 16);
|
|
1542
1959
|
return /* @__PURE__ */ jsxs8(ClickableBox, { onClick: () => onSelect(i), marginRight: 2, children: [
|
|
1543
|
-
/* @__PURE__ */
|
|
1544
|
-
/* @__PURE__ */
|
|
1545
|
-
/* @__PURE__ */
|
|
1546
|
-
/* @__PURE__ */
|
|
1547
|
-
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 })
|
|
1548
1965
|
] }, s.id ?? "__all__");
|
|
1549
1966
|
}) });
|
|
1550
1967
|
});
|
|
@@ -1597,7 +2014,7 @@ function osc8(text, url) {
|
|
|
1597
2014
|
}
|
|
1598
2015
|
|
|
1599
2016
|
// src/ui/footer.tsx
|
|
1600
|
-
import { jsx as
|
|
2017
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1601
2018
|
var Footer = memo6(function Footer2({ hasAccounts, paginated, cols }) {
|
|
1602
2019
|
const inner = cols - 4;
|
|
1603
2020
|
const BASE = "by David Ilie (davidilie.com) \xB7 r/R=refresh O=repo W=web s=settings q=quit".length;
|
|
@@ -1609,22 +2026,22 @@ var Footer = memo6(function Footer2({ hasAccounts, paginated, cols }) {
|
|
|
1609
2026
|
const showJump = hasAccounts && inner >= BASE + (showOpt ? OPT : 0) + JUMP + (paginated ? PAGE : 0);
|
|
1610
2027
|
const showPage = paginated && inner >= BASE + (showOpt ? OPT : 0) + (showJump ? JUMP : 0) + PAGE;
|
|
1611
2028
|
if (inner < BASE) {
|
|
1612
|
-
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" }) });
|
|
1613
2030
|
}
|
|
1614
2031
|
return /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexWrap: "nowrap", children: [
|
|
1615
|
-
/* @__PURE__ */
|
|
1616
|
-
/* @__PURE__ */
|
|
1617
|
-
/* @__PURE__ */
|
|
1618
|
-
/* @__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" }) }) }),
|
|
1619
2036
|
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
1620
2037
|
") ",
|
|
1621
2038
|
glyphs().middot,
|
|
1622
2039
|
" r/R=refresh O=repo W=web s=settings "
|
|
1623
2040
|
] }),
|
|
1624
|
-
showOpt && /* @__PURE__ */
|
|
1625
|
-
showJump && /* @__PURE__ */
|
|
1626
|
-
showPage && /* @__PURE__ */
|
|
1627
|
-
/* @__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" })
|
|
1628
2045
|
] });
|
|
1629
2046
|
});
|
|
1630
2047
|
|
|
@@ -1634,16 +2051,17 @@ import { Box as Box10, Text as Text11 } from "ink";
|
|
|
1634
2051
|
// src/ui/refresh-status.tsx
|
|
1635
2052
|
import { memo as memo7 } from "react";
|
|
1636
2053
|
import { Text as Text10 } from "ink";
|
|
1637
|
-
import { jsx as
|
|
2054
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1638
2055
|
var RefreshStatusLine = memo7(function RefreshStatusLine2({ status }) {
|
|
2056
|
+
const theme = useTuiTheme();
|
|
1639
2057
|
if (status.phase === "idle") return null;
|
|
1640
|
-
if (status.phase === "refreshing") return /* @__PURE__ */
|
|
1641
|
-
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: [
|
|
1642
2060
|
glyphs().check,
|
|
1643
2061
|
" ",
|
|
1644
2062
|
status.message
|
|
1645
2063
|
] });
|
|
1646
|
-
return /* @__PURE__ */ jsxs10(Text10, { color:
|
|
2064
|
+
return /* @__PURE__ */ jsxs10(Text10, { color: theme.crit, children: [
|
|
1647
2065
|
glyphs().warn,
|
|
1648
2066
|
" ",
|
|
1649
2067
|
status.message
|
|
@@ -1651,14 +2069,15 @@ var RefreshStatusLine = memo7(function RefreshStatusLine2({ status }) {
|
|
|
1651
2069
|
});
|
|
1652
2070
|
|
|
1653
2071
|
// src/ui/tiny-fallback.tsx
|
|
1654
|
-
import { jsx as
|
|
2072
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1655
2073
|
function TinyFallback({ groups, stats, rows, cols, refreshStatus }) {
|
|
2074
|
+
const theme = useTuiTheme();
|
|
1656
2075
|
const maxLines = Math.max(1, rows - 4);
|
|
1657
2076
|
const visible = groups.slice(0, maxLines);
|
|
1658
2077
|
const hidden = groups.length - visible.length;
|
|
1659
2078
|
const w = Math.max(8, cols - 2);
|
|
1660
2079
|
return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", paddingX: 1, height: rows, overflow: "hidden", children: [
|
|
1661
|
-
/* @__PURE__ */ jsxs11(Text11, { bold: true, color:
|
|
2080
|
+
/* @__PURE__ */ jsxs11(Text11, { bold: true, color: theme.accent, children: [
|
|
1662
2081
|
glyphs().dotSel,
|
|
1663
2082
|
" tokmon"
|
|
1664
2083
|
] }),
|
|
@@ -1666,15 +2085,15 @@ function TinyFallback({ groups, stats, rows, cols, refreshStatus }) {
|
|
|
1666
2085
|
"No providers ",
|
|
1667
2086
|
glyphs().emDash,
|
|
1668
2087
|
" s=settings"
|
|
1669
|
-
] }) : visible.map((g) => /* @__PURE__ */
|
|
2088
|
+
] }) : visible.map((g) => /* @__PURE__ */ jsx12(TinyRow, { provider: g.provider, accounts: g.accounts, stats, width: w }, g.provider)),
|
|
1670
2089
|
hidden > 0 && /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
1671
2090
|
"+",
|
|
1672
2091
|
hidden,
|
|
1673
2092
|
" more (enlarge terminal)"
|
|
1674
2093
|
] }),
|
|
1675
|
-
/* @__PURE__ */
|
|
1676
|
-
/* @__PURE__ */
|
|
1677
|
-
/* @__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" })
|
|
1678
2097
|
] });
|
|
1679
2098
|
}
|
|
1680
2099
|
function TinyRow({ provider, accounts, stats, width }) {
|
|
@@ -1690,19 +2109,19 @@ function TinyRow({ provider, accounts, stats, width }) {
|
|
|
1690
2109
|
glyphs().dot,
|
|
1691
2110
|
" "
|
|
1692
2111
|
] }),
|
|
1693
|
-
/* @__PURE__ */
|
|
1694
|
-
/* @__PURE__ */
|
|
1695
|
-
/* @__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 })
|
|
1696
2115
|
] });
|
|
1697
2116
|
}
|
|
1698
2117
|
|
|
1699
2118
|
// src/client/use-daemon.ts
|
|
1700
|
-
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";
|
|
1701
2120
|
function useDaemon(baseUrl) {
|
|
1702
2121
|
const [snapshot, setSnapshot] = useState3(null);
|
|
1703
2122
|
const [conn, setConn] = useState3("connecting");
|
|
1704
2123
|
const [config, setConfigState] = useState3(null);
|
|
1705
|
-
const client =
|
|
2124
|
+
const client = useMemo2(() => {
|
|
1706
2125
|
if (!baseUrl) return null;
|
|
1707
2126
|
return createDaemonRpcClient(baseUrl, {
|
|
1708
2127
|
transport: "node",
|
|
@@ -1745,6 +2164,7 @@ function indexById(snapshot) {
|
|
|
1745
2164
|
}
|
|
1746
2165
|
function toStatsMap(snapshot, accounts) {
|
|
1747
2166
|
const byId = indexById(snapshot);
|
|
2167
|
+
const providerHeadroom = new Map(snapshot?.providers.map((provider) => [provider.id, provider.headroom]) ?? []);
|
|
1748
2168
|
const out = /* @__PURE__ */ new Map();
|
|
1749
2169
|
for (const account of accounts) {
|
|
1750
2170
|
const wa = byId.get(account.id);
|
|
@@ -1752,7 +2172,11 @@ function toStatsMap(snapshot, accounts) {
|
|
|
1752
2172
|
account,
|
|
1753
2173
|
dashboard: wa?.dashboard ?? null,
|
|
1754
2174
|
billing: wa?.billing ?? null,
|
|
1755
|
-
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)
|
|
1756
2180
|
});
|
|
1757
2181
|
}
|
|
1758
2182
|
return out;
|
|
@@ -1778,13 +2202,13 @@ var SORTS = [
|
|
|
1778
2202
|
{ label: "cost", dir: "down" }
|
|
1779
2203
|
];
|
|
1780
2204
|
var acctKey = (a) => `${a.id}:${a.homeDir ?? ""}`;
|
|
1781
|
-
var clampCaret = (
|
|
1782
|
-
function spliceInsert(value,
|
|
1783
|
-
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);
|
|
1784
2208
|
return { value: value.slice(0, c) + text + value.slice(c), caret: c + text.length };
|
|
1785
2209
|
}
|
|
1786
|
-
function spliceBackspace(value,
|
|
1787
|
-
const c = clampCaret(
|
|
2210
|
+
function spliceBackspace(value, caret2) {
|
|
2211
|
+
const c = clampCaret(caret2, value.length);
|
|
1788
2212
|
if (c === 0) return { value, caret: 0 };
|
|
1789
2213
|
return { value: value.slice(0, c - 1) + value.slice(c), caret: c - 1 };
|
|
1790
2214
|
}
|
|
@@ -1984,11 +2408,11 @@ function handleTimezoneEditor(input, key, editor, text, updateConfig) {
|
|
|
1984
2408
|
return;
|
|
1985
2409
|
}
|
|
1986
2410
|
if (key.leftArrow) {
|
|
1987
|
-
setCaret((
|
|
2411
|
+
setCaret((caret2) => clampCaret(caret2 - 1, value.length));
|
|
1988
2412
|
return;
|
|
1989
2413
|
}
|
|
1990
2414
|
if (key.rightArrow) {
|
|
1991
|
-
setCaret((
|
|
2415
|
+
setCaret((caret2) => clampCaret(caret2 + 1, value.length));
|
|
1992
2416
|
return;
|
|
1993
2417
|
}
|
|
1994
2418
|
if (key.ctrl && input === "a") {
|
|
@@ -2034,11 +2458,11 @@ function handleAllowedHostsEditor(input, key, editor, text, updateConfig) {
|
|
|
2034
2458
|
return;
|
|
2035
2459
|
}
|
|
2036
2460
|
if (key.leftArrow) {
|
|
2037
|
-
setCaret((
|
|
2461
|
+
setCaret((caret2) => clampCaret(caret2 - 1, value.length));
|
|
2038
2462
|
return;
|
|
2039
2463
|
}
|
|
2040
2464
|
if (key.rightArrow) {
|
|
2041
|
-
setCaret((
|
|
2465
|
+
setCaret((caret2) => clampCaret(caret2 + 1, value.length));
|
|
2042
2466
|
return;
|
|
2043
2467
|
}
|
|
2044
2468
|
if (key.ctrl && input === "a") {
|
|
@@ -2074,11 +2498,11 @@ function handleSearchEditor(input, key, table, text) {
|
|
|
2074
2498
|
return;
|
|
2075
2499
|
}
|
|
2076
2500
|
if (key.leftArrow) {
|
|
2077
|
-
setSearchCaret((
|
|
2501
|
+
setSearchCaret((caret2) => clampCaret(caret2 - 1, search.length));
|
|
2078
2502
|
return;
|
|
2079
2503
|
}
|
|
2080
2504
|
if (key.rightArrow) {
|
|
2081
|
-
setSearchCaret((
|
|
2505
|
+
setSearchCaret((caret2) => clampCaret(caret2 + 1, search.length));
|
|
2082
2506
|
return;
|
|
2083
2507
|
}
|
|
2084
2508
|
if (key.ctrl && input === "a") {
|
|
@@ -2138,7 +2562,7 @@ function handleSettings(input, key, ctx) {
|
|
|
2138
2562
|
ctx.allowedHostsEditor.setValue(null);
|
|
2139
2563
|
ctx.allowedHostsEditor.setError(null);
|
|
2140
2564
|
};
|
|
2141
|
-
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;
|
|
2142
2566
|
if (key.tab) {
|
|
2143
2567
|
switchTab(key.shift ? -1 : 1);
|
|
2144
2568
|
return;
|
|
@@ -2184,17 +2608,64 @@ function handleSettings(input, key, ctx) {
|
|
|
2184
2608
|
return;
|
|
2185
2609
|
}
|
|
2186
2610
|
if (tab === "general") {
|
|
2187
|
-
|
|
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
|
+
}
|
|
2188
2633
|
return;
|
|
2189
2634
|
}
|
|
2190
2635
|
if (tab === "providers") {
|
|
2191
|
-
if (cursor
|
|
2192
|
-
|
|
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);
|
|
2193
2657
|
}
|
|
2194
2658
|
return;
|
|
2195
2659
|
}
|
|
2196
2660
|
if (cursor < trackedAccounts.length) {
|
|
2197
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
|
+
}
|
|
2198
2669
|
if (key.return) {
|
|
2199
2670
|
if (row.source === "configured") {
|
|
2200
2671
|
const account = config.accounts.find((candidate) => candidate.id === row.explicitId);
|
|
@@ -2208,6 +2679,17 @@ function handleSettings(input, key, ctx) {
|
|
|
2208
2679
|
deleteAccount(row.explicitId);
|
|
2209
2680
|
return;
|
|
2210
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
|
+
}
|
|
2211
2693
|
if (input === " ") {
|
|
2212
2694
|
updateConfig((current) => ({ ...current, activeAccountId: row.id }));
|
|
2213
2695
|
return;
|
|
@@ -2215,69 +2697,6 @@ function handleSettings(input, key, ctx) {
|
|
|
2215
2697
|
return;
|
|
2216
2698
|
}
|
|
2217
2699
|
if (cursor === trackedAccounts.length && key.return) openAddAccount();
|
|
2218
|
-
function handleGeneralSetting(value, inputKey, context) {
|
|
2219
|
-
const index = context.settings.cursor;
|
|
2220
|
-
if (index === 0) {
|
|
2221
|
-
if (inputKey.leftArrow) updateConfig((current) => ({ ...current, interval: Math.max(1, current.interval - 1) }));
|
|
2222
|
-
if (inputKey.rightArrow) updateConfig((current) => ({ ...current, interval: current.interval + 1 }));
|
|
2223
|
-
return;
|
|
2224
|
-
}
|
|
2225
|
-
if (index === 1) {
|
|
2226
|
-
if (inputKey.leftArrow) updateConfig((current) => ({ ...current, billingInterval: Math.max(1, current.billingInterval - 1) }));
|
|
2227
|
-
if (inputKey.rightArrow) updateConfig((current) => ({ ...current, billingInterval: current.billingInterval + 1 }));
|
|
2228
|
-
return;
|
|
2229
|
-
}
|
|
2230
|
-
if (index === 2 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2231
|
-
updateConfig((current) => ({ ...current, clearScreen: !current.clearScreen }));
|
|
2232
|
-
return;
|
|
2233
|
-
}
|
|
2234
|
-
if (index === 3 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2235
|
-
updateConfig((current) => ({ ...current, privacyMode: !current.privacyMode }));
|
|
2236
|
-
return;
|
|
2237
|
-
}
|
|
2238
|
-
if (index === 4) {
|
|
2239
|
-
if (textInput.isPrintable(value, inputKey)) {
|
|
2240
|
-
const clean = sanitizeTyped(value);
|
|
2241
|
-
if (clean.length === 1) updateConfig((current) => ({ ...current, privacyToggleKey: clean }));
|
|
2242
|
-
}
|
|
2243
|
-
if (inputKey.backspace || inputKey.delete) updateConfig((current) => ({ ...current, privacyToggleKey: "p" }));
|
|
2244
|
-
return;
|
|
2245
|
-
}
|
|
2246
|
-
if (index === 5) {
|
|
2247
|
-
if (inputKey.return) {
|
|
2248
|
-
const initial = config.timezone ?? "";
|
|
2249
|
-
timezoneEditor.setValue(initial);
|
|
2250
|
-
timezoneEditor.setCaret(initial.length);
|
|
2251
|
-
timezoneEditor.setError(null);
|
|
2252
|
-
}
|
|
2253
|
-
if (inputKey.leftArrow || inputKey.rightArrow) {
|
|
2254
|
-
updateConfig((current) => ({ ...current, timezone: current.timezone === null ? systemTimezone() : null }));
|
|
2255
|
-
}
|
|
2256
|
-
return;
|
|
2257
|
-
}
|
|
2258
|
-
if (index === 6 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2259
|
-
updateConfig((current) => ({ ...current, dashboardLayout: current.dashboardLayout === "grid" ? "single" : "grid" }));
|
|
2260
|
-
return;
|
|
2261
|
-
}
|
|
2262
|
-
if (index === 7 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2263
|
-
updateConfig((current) => ({ ...current, defaultFocus: current.defaultFocus === "all" ? "last" : "all" }));
|
|
2264
|
-
return;
|
|
2265
|
-
}
|
|
2266
|
-
if (index === 8 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2267
|
-
updateConfig((current) => ({ ...current, allowNetworkAccess: !current.allowNetworkAccess }));
|
|
2268
|
-
return;
|
|
2269
|
-
}
|
|
2270
|
-
if (index === 9 && inputKey.return) {
|
|
2271
|
-
const initial = config.allowedHosts.join(", ");
|
|
2272
|
-
context.allowedHostsEditor.setValue(initial);
|
|
2273
|
-
context.allowedHostsEditor.setCaret(initial.length);
|
|
2274
|
-
context.allowedHostsEditor.setError(null);
|
|
2275
|
-
return;
|
|
2276
|
-
}
|
|
2277
|
-
if (index === 10 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
|
|
2278
|
-
updateConfig((current) => ({ ...current, resetDisplay: current.resetDisplay === "relative" ? "absolute" : "relative" }));
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
2700
|
}
|
|
2282
2701
|
|
|
2283
2702
|
// src/ui/keybindings-navigation.ts
|
|
@@ -2346,7 +2765,8 @@ function handleNavigation(input, key, ctx) {
|
|
|
2346
2765
|
}
|
|
2347
2766
|
if (table.tab !== 1) {
|
|
2348
2767
|
if (key.leftArrow || key.rightArrow) {
|
|
2349
|
-
|
|
2768
|
+
const step2 = key.leftArrow ? -1 : 1;
|
|
2769
|
+
global.setTab((current) => (current + step2 + TABS.length) % TABS.length);
|
|
2350
2770
|
resetView();
|
|
2351
2771
|
}
|
|
2352
2772
|
return;
|
|
@@ -3149,8 +3569,8 @@ function useAccountForm({ cfg, detected, updateConfig, trackedAccountRows, setSe
|
|
|
3149
3569
|
if (!f) return f;
|
|
3150
3570
|
const i = FORM_FIELDS.indexOf(f.field);
|
|
3151
3571
|
const next = FORM_FIELDS[(i + dir + FORM_FIELDS.length) % FORM_FIELDS.length];
|
|
3152
|
-
const
|
|
3153
|
-
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 };
|
|
3154
3574
|
});
|
|
3155
3575
|
}, []);
|
|
3156
3576
|
const cycleProvider = useCallback6((dir) => {
|
|
@@ -3201,7 +3621,7 @@ function useAccountForm({ cfg, detected, updateConfig, trackedAccountRows, setSe
|
|
|
3201
3621
|
}
|
|
3202
3622
|
|
|
3203
3623
|
// src/app.tsx
|
|
3204
|
-
import { Fragment as
|
|
3624
|
+
import { Fragment as Fragment6, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3205
3625
|
function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "degraded" }) {
|
|
3206
3626
|
const connected = mode === "connected" && baseUrl !== null;
|
|
3207
3627
|
const degraded = !connected;
|
|
@@ -3247,9 +3667,24 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3247
3667
|
const billingMs = cfg.billingInterval * 6e4;
|
|
3248
3668
|
const tz = resolveTimezone(cfg.timezone);
|
|
3249
3669
|
const configReady = config !== null;
|
|
3250
|
-
const
|
|
3251
|
-
const
|
|
3252
|
-
|
|
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;
|
|
3253
3688
|
const accountsRef = useRef8([]);
|
|
3254
3689
|
accountsRef.current = accounts;
|
|
3255
3690
|
const rowCountRef = useRef8(0);
|
|
@@ -3262,19 +3697,19 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3262
3697
|
const allowedHostsCaretRef = useRef8(0);
|
|
3263
3698
|
const searchValueRef = useRef8("");
|
|
3264
3699
|
const searchCaretRef = useRef8(0);
|
|
3265
|
-
const accountsKey =
|
|
3266
|
-
const allGroups =
|
|
3267
|
-
const tableProvs =
|
|
3700
|
+
const accountsKey = useMemo3(() => accounts.map(acctKey).join("|"), [accounts]);
|
|
3701
|
+
const allGroups = useMemo3(() => accountsByProvider(accounts), [accounts]);
|
|
3702
|
+
const tableProvs = useMemo3(
|
|
3268
3703
|
() => allGroups.map((g) => g.provider).filter((pid) => PROVIDERS[pid].hasUsage),
|
|
3269
3704
|
[allGroups]
|
|
3270
3705
|
);
|
|
3271
3706
|
const effTableProvider = tableProvider && tableProvs.includes(tableProvider) ? tableProvider : tableProvs[0] ?? null;
|
|
3272
|
-
const tableAccounts =
|
|
3707
|
+
const tableAccounts = useMemo3(
|
|
3273
3708
|
() => effTableProvider ? accounts.filter((a) => a.providerId === effTableProvider) : [],
|
|
3274
3709
|
[accounts, effTableProvider]
|
|
3275
3710
|
);
|
|
3276
3711
|
const needsOnboarding = configReady && !cfg.onboarded;
|
|
3277
|
-
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)) : [];
|
|
3278
3713
|
const showPicker = needsOnboarding || newProviders.length > 0;
|
|
3279
3714
|
const {
|
|
3280
3715
|
statsLocal,
|
|
@@ -3295,7 +3730,6 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3295
3730
|
activeTableProvider: effTableProvider,
|
|
3296
3731
|
tableVisible: tab === 1
|
|
3297
3732
|
});
|
|
3298
|
-
const snapshot = daemon.snapshot;
|
|
3299
3733
|
const daemonRefreshRef = useRef8(daemon.refresh);
|
|
3300
3734
|
const degradedRefreshRef = useRef8(refreshAllDegraded);
|
|
3301
3735
|
daemonRefreshRef.current = daemon.refresh;
|
|
@@ -3307,11 +3741,11 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3307
3741
|
requestDaemonRefresh,
|
|
3308
3742
|
requestDegradedRefresh
|
|
3309
3743
|
});
|
|
3310
|
-
const stats =
|
|
3744
|
+
const stats = useMemo3(
|
|
3311
3745
|
() => connected ? toStatsMap(snapshot, accounts) : statsLocal,
|
|
3312
3746
|
[connected, snapshot, accounts, statsLocal]
|
|
3313
3747
|
);
|
|
3314
|
-
const accountIdentities =
|
|
3748
|
+
const accountIdentities = useMemo3(() => {
|
|
3315
3749
|
const out = /* @__PURE__ */ new Map();
|
|
3316
3750
|
for (const [id, stat] of stats) {
|
|
3317
3751
|
const billing = stat.billing;
|
|
@@ -3326,7 +3760,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3326
3760
|
}, [stats]);
|
|
3327
3761
|
const showPeak = accounts.some((a) => a.providerId === "claude");
|
|
3328
3762
|
const peak = connected ? showPeak ? snapshot?.peak ?? null : null : peakLocal;
|
|
3329
|
-
const updated =
|
|
3763
|
+
const updated = useMemo3(
|
|
3330
3764
|
() => connected ? new Date(snapshot?.generatedAt ?? Date.now()) : updatedLocal,
|
|
3331
3765
|
[connected, snapshot, updatedLocal]
|
|
3332
3766
|
);
|
|
@@ -3340,23 +3774,23 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3340
3774
|
}
|
|
3341
3775
|
return statsReadyInput(statsLocal.get(id));
|
|
3342
3776
|
}, [connected, snapshot, statsLocal]);
|
|
3343
|
-
const slots =
|
|
3344
|
-
const { activeSlotIdx, focusId } =
|
|
3777
|
+
const slots = useMemo3(() => deriveSlots(accounts, cfg.privacyMode), [accounts, cfg.privacyMode]);
|
|
3778
|
+
const { activeSlotIdx, focusId } = useMemo3(
|
|
3345
3779
|
() => findActiveSlot(slots, cfg.activeAccountId),
|
|
3346
3780
|
[slots, cfg.activeAccountId]
|
|
3347
3781
|
);
|
|
3348
|
-
const visibleAccounts =
|
|
3782
|
+
const visibleAccounts = useMemo3(
|
|
3349
3783
|
() => focusId === null ? accounts : accounts.filter((a) => a.id === focusId),
|
|
3350
3784
|
[accounts, focusId]
|
|
3351
3785
|
);
|
|
3352
|
-
const groups =
|
|
3786
|
+
const groups = useMemo3(
|
|
3353
3787
|
() => focusId === null ? allGroups : accountsByProvider(visibleAccounts),
|
|
3354
3788
|
[allGroups, visibleAccounts, focusId]
|
|
3355
3789
|
);
|
|
3356
3790
|
const TOO_SMALL = cols < 40 || rows < 12;
|
|
3357
3791
|
const allReady = accounts.length > 0 && accounts.every((a) => accountReady(readyInputFor(a.id), a.providerId));
|
|
3358
|
-
const { gridBudget } =
|
|
3359
|
-
const dashLayout =
|
|
3792
|
+
const { gridBudget } = useMemo3(() => computeChrome(slots, cols, rows), [slots, cols, rows]);
|
|
3793
|
+
const dashLayout = useMemo3(
|
|
3360
3794
|
() => computeDashLayout(groups, stats, cols, gridBudget, focusId, cfg.dashboardLayout),
|
|
3361
3795
|
[groups, stats, cols, gridBudget, focusId, cfg.dashboardLayout]
|
|
3362
3796
|
);
|
|
@@ -3401,8 +3835,8 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3401
3835
|
};
|
|
3402
3836
|
const { handlePasteData, isPasteInput } = usePaste(insertText);
|
|
3403
3837
|
const SORTS_FOR = SORTS;
|
|
3404
|
-
const tableAccountIds =
|
|
3405
|
-
const table =
|
|
3838
|
+
const tableAccountIds = useMemo3(() => tableAccounts.map((a) => a.id), [tableAccounts]);
|
|
3839
|
+
const table = useMemo3(
|
|
3406
3840
|
() => connected ? pickTable(snapshot, tableAccountIds) : tableLocal,
|
|
3407
3841
|
[connected, snapshot, tableAccountIds, tableLocal]
|
|
3408
3842
|
);
|
|
@@ -3416,18 +3850,19 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3416
3850
|
accountsCount: accounts.length
|
|
3417
3851
|
});
|
|
3418
3852
|
const pickerProviders = needsOnboarding ? PROVIDER_ORDER : newProviders;
|
|
3419
|
-
const onboardEnabled = onboardSel ??
|
|
3853
|
+
const onboardEnabled = onboardSel ?? detectedProviders;
|
|
3420
3854
|
const onboardItems = pickerProviders.map((pid) => ({
|
|
3421
3855
|
id: pid,
|
|
3422
3856
|
name: PROVIDERS[pid].name,
|
|
3423
3857
|
color: PROVIDERS[pid].color,
|
|
3424
|
-
detected:
|
|
3858
|
+
detected: detectedProviders.includes(pid),
|
|
3425
3859
|
enabled: onboardEnabled.includes(pid)
|
|
3426
3860
|
}));
|
|
3427
3861
|
useEffect9(() => {
|
|
3862
|
+
if (connected) return;
|
|
3428
3863
|
void detectProviders().then(setDetected);
|
|
3429
|
-
}, []);
|
|
3430
|
-
const tableKey =
|
|
3864
|
+
}, [connected]);
|
|
3865
|
+
const tableKey = useMemo3(
|
|
3431
3866
|
() => `${effTableProvider}|${tableAccounts.map(acctKey).join(",")}|${tz}`,
|
|
3432
3867
|
[effTableProvider, tableAccounts, tz]
|
|
3433
3868
|
);
|
|
@@ -3485,7 +3920,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3485
3920
|
if (i < 0 || i >= pickerProviders.length) return;
|
|
3486
3921
|
const pid = pickerProviders[i];
|
|
3487
3922
|
setOnboardSel((prev) => {
|
|
3488
|
-
const base = prev ??
|
|
3923
|
+
const base = prev ?? detectedProviders;
|
|
3489
3924
|
return base.includes(pid) ? base.filter((p) => p !== pid) : [...base, pid];
|
|
3490
3925
|
});
|
|
3491
3926
|
}
|
|
@@ -3548,7 +3983,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3548
3983
|
cycleColor,
|
|
3549
3984
|
deleteAccount,
|
|
3550
3985
|
moveAccount
|
|
3551
|
-
} = useAccountForm({ cfg, detected, updateConfig, trackedAccountRows, setSettingsCursor });
|
|
3986
|
+
} = useAccountForm({ cfg, detected: detectedProviders, updateConfig, trackedAccountRows, setSettingsCursor });
|
|
3552
3987
|
async function toggleWeb() {
|
|
3553
3988
|
if (connected) {
|
|
3554
3989
|
if (baseUrl) openUrl(baseUrl);
|
|
@@ -3561,7 +3996,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3561
3996
|
if (webStartingRef.current) return;
|
|
3562
3997
|
webStartingRef.current = true;
|
|
3563
3998
|
try {
|
|
3564
|
-
const { startWebServer } = await import("./server-
|
|
3999
|
+
const { startWebServer } = await import("./server-EPSH52DR.js");
|
|
3565
4000
|
const ctrl = await startWebServer({ config: cfg, log: false });
|
|
3566
4001
|
webRef.current = ctrl;
|
|
3567
4002
|
openUrl(ctrl.url);
|
|
@@ -3591,11 +4026,11 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3591
4026
|
if (idx === cursor) setExpanded((e) => e === idx ? -1 : idx);
|
|
3592
4027
|
else setCursor(idx);
|
|
3593
4028
|
}, [cursor]);
|
|
3594
|
-
const rawTokenRows =
|
|
4029
|
+
const rawTokenRows = useMemo3(
|
|
3595
4030
|
() => tab === 1 ? table ? [table.daily, table.weekly, table.monthly][view] : [] : [],
|
|
3596
4031
|
[tab, table, view]
|
|
3597
4032
|
);
|
|
3598
|
-
const tokenModelOptions =
|
|
4033
|
+
const tokenModelOptions = useMemo3(() => tableModelOptions(rawTokenRows), [rawTokenRows]);
|
|
3599
4034
|
useEffect9(() => {
|
|
3600
4035
|
if (tableModel && !tokenModelOptions.includes(tableModel)) setTableModel(null);
|
|
3601
4036
|
}, [tableModel, tokenModelOptions]);
|
|
@@ -3604,7 +4039,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3604
4039
|
resetView();
|
|
3605
4040
|
}, [tokenModelOptions, resetView]);
|
|
3606
4041
|
const activeTableModel = tableModel && tokenModelOptions.includes(tableModel) ? tableModel : null;
|
|
3607
|
-
const tokenRows =
|
|
4042
|
+
const tokenRows = useMemo3(
|
|
3608
4043
|
() => tab === 1 ? sortRows(filterTokenRows(filterRowsByModel(rawTokenRows, activeTableModel), search), sort) : [],
|
|
3609
4044
|
[tab, rawTokenRows, activeTableModel, search, sort]
|
|
3610
4045
|
);
|
|
@@ -3696,10 +4131,10 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3696
4131
|
}
|
|
3697
4132
|
});
|
|
3698
4133
|
}, { isActive: IS_TTY });
|
|
3699
|
-
if (!config) return /* @__PURE__ */
|
|
3700
|
-
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 }) });
|
|
3701
4136
|
if (showPicker) {
|
|
3702
|
-
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(
|
|
3703
4138
|
Onboarding,
|
|
3704
4139
|
{
|
|
3705
4140
|
items: onboardItems,
|
|
@@ -3709,22 +4144,19 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3709
4144
|
heading: needsOnboarding ? "Welcome to tokmon" : "New providers detected",
|
|
3710
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."
|
|
3711
4146
|
}
|
|
3712
|
-
) });
|
|
4147
|
+
) }) });
|
|
3713
4148
|
}
|
|
3714
4149
|
if (showLoader) {
|
|
3715
|
-
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 }) }) });
|
|
3716
4151
|
}
|
|
3717
4152
|
if (TOO_SMALL && !showSettings) {
|
|
3718
|
-
return /* @__PURE__ */
|
|
4153
|
+
return /* @__PURE__ */ jsx13(TuiThemeProvider, { appearance: cfg.appearance, children: /* @__PURE__ */ jsx13(TinyFallback, { groups, stats, rows, cols, refreshStatus }) });
|
|
3719
4154
|
}
|
|
3720
4155
|
rowCountRef.current = tokenRows.length;
|
|
3721
|
-
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: [
|
|
3722
4157
|
/* @__PURE__ */ jsxs12(Box11, { justifyContent: "space-between", children: [
|
|
3723
4158
|
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
3724
|
-
/* @__PURE__ */
|
|
3725
|
-
glyphs().dotSel,
|
|
3726
|
-
" tokmon"
|
|
3727
|
-
] }),
|
|
4159
|
+
/* @__PURE__ */ jsx13(ThemedBrand, {}),
|
|
3728
4160
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
3729
4161
|
" ",
|
|
3730
4162
|
glyphs().middot,
|
|
@@ -3738,15 +4170,15 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3738
4170
|
] })
|
|
3739
4171
|
] }),
|
|
3740
4172
|
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
3741
|
-
peak && /* @__PURE__ */ jsxs12(
|
|
3742
|
-
/* @__PURE__ */
|
|
4173
|
+
peak && /* @__PURE__ */ jsxs12(Fragment6, { children: [
|
|
4174
|
+
/* @__PURE__ */ jsx13(PeakBadge, { peak, resetDisplay: cfg.resetDisplay, tz }),
|
|
3743
4175
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
3744
4176
|
" ",
|
|
3745
4177
|
glyphs().middot,
|
|
3746
4178
|
" "
|
|
3747
4179
|
] })
|
|
3748
4180
|
] }),
|
|
3749
|
-
/* @__PURE__ */
|
|
4181
|
+
/* @__PURE__ */ jsx13(Text12, { dimColor: true, children: time(updated, tz) })
|
|
3750
4182
|
] })
|
|
3751
4183
|
] }),
|
|
3752
4184
|
degraded && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
@@ -3761,19 +4193,19 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3761
4193
|
glyphs().middot,
|
|
3762
4194
|
" showing last known data"
|
|
3763
4195
|
] }),
|
|
3764
|
-
cfg.allowNetworkAccess && /* @__PURE__ */ jsxs12(
|
|
4196
|
+
cfg.allowNetworkAccess && /* @__PURE__ */ jsxs12(ThemedError, { children: [
|
|
3765
4197
|
glyphs().warn,
|
|
3766
4198
|
" unsafe network access enabled ",
|
|
3767
4199
|
glyphs().middot,
|
|
3768
4200
|
" dashboard may be reachable from your LAN after daemon restart"
|
|
3769
4201
|
] }),
|
|
3770
|
-
configSaveError && /* @__PURE__ */ jsxs12(
|
|
4202
|
+
configSaveError && /* @__PURE__ */ jsxs12(ThemedError, { children: [
|
|
3771
4203
|
glyphs().warn,
|
|
3772
4204
|
" ",
|
|
3773
4205
|
configSaveError
|
|
3774
4206
|
] }),
|
|
3775
|
-
/* @__PURE__ */
|
|
3776
|
-
showSettings ? /* @__PURE__ */
|
|
4207
|
+
/* @__PURE__ */ jsx13(RefreshStatusLine, { status: refreshStatus }),
|
|
4208
|
+
showSettings ? /* @__PURE__ */ jsx13(
|
|
3777
4209
|
SettingsView,
|
|
3778
4210
|
{
|
|
3779
4211
|
config: cfg,
|
|
@@ -3791,20 +4223,20 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3791
4223
|
trackedAccounts: trackedAccountRows,
|
|
3792
4224
|
accountIdentities
|
|
3793
4225
|
}
|
|
3794
|
-
) : /* @__PURE__ */ jsxs12(
|
|
4226
|
+
) : /* @__PURE__ */ jsxs12(Fragment6, { children: [
|
|
3795
4227
|
/* @__PURE__ */ jsxs12(Box11, { marginTop: 1, marginBottom: 1, children: [
|
|
3796
|
-
/* @__PURE__ */
|
|
4228
|
+
/* @__PURE__ */ jsx13(TabBar, { tabs: TABS, active: tab, onSelect: onTabSelect }),
|
|
3797
4229
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
3798
4230
|
" Tab/",
|
|
3799
4231
|
glyphs().arrowL,
|
|
3800
4232
|
glyphs().arrowR
|
|
3801
4233
|
] })
|
|
3802
4234
|
] }),
|
|
3803
|
-
tab === 0 && /* @__PURE__ */ jsxs12(
|
|
3804
|
-
/* @__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 }),
|
|
3805
4237
|
slots.length > 1 && /* @__PURE__ */ jsxs12(Box11, { marginTop: 1, children: [
|
|
3806
|
-
/* @__PURE__ */
|
|
3807
|
-
/* @__PURE__ */
|
|
4238
|
+
/* @__PURE__ */ jsx13(Text12, { dimColor: true, children: "focus " }),
|
|
4239
|
+
/* @__PURE__ */ jsx13(
|
|
3808
4240
|
AccountStrip,
|
|
3809
4241
|
{
|
|
3810
4242
|
slots,
|
|
@@ -3813,12 +4245,12 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3813
4245
|
}
|
|
3814
4246
|
)
|
|
3815
4247
|
] }),
|
|
3816
|
-
/* @__PURE__ */
|
|
4248
|
+
/* @__PURE__ */ jsx13(TotalsRow, { groups, stats, cols })
|
|
3817
4249
|
] }),
|
|
3818
|
-
tab === 1 && /* @__PURE__ */ jsxs12(
|
|
3819
|
-
tableProvs.length > 0 && /* @__PURE__ */
|
|
3820
|
-
/* @__PURE__ */
|
|
3821
|
-
/* @__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(
|
|
3822
4254
|
ControlBar,
|
|
3823
4255
|
{
|
|
3824
4256
|
views: VIEWS,
|
|
@@ -3832,12 +4264,12 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3832
4264
|
showModel: true
|
|
3833
4265
|
}
|
|
3834
4266
|
),
|
|
3835
|
-
/* @__PURE__ */
|
|
4267
|
+
/* @__PURE__ */ jsx13(Box11, { height: 1 }),
|
|
3836
4268
|
!effTableProvider ? /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
3837
4269
|
"No providers enabled ",
|
|
3838
4270
|
glyphs().emDash,
|
|
3839
4271
|
" press s to pick providers."
|
|
3840
|
-
] }) : tableLoading && !table ? /* @__PURE__ */
|
|
4272
|
+
] }) : tableLoading && !table ? /* @__PURE__ */ jsx13(Spinner, { label: "Loading history" }) : /* @__PURE__ */ jsx13(
|
|
3841
4273
|
TokenTable,
|
|
3842
4274
|
{
|
|
3843
4275
|
rows: tokenRows,
|
|
@@ -3850,12 +4282,23 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
|
|
|
3850
4282
|
)
|
|
3851
4283
|
] })
|
|
3852
4284
|
] }),
|
|
3853
|
-
(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"
|
|
3854
4293
|
] });
|
|
3855
4294
|
}
|
|
4295
|
+
function ThemedError({ children }) {
|
|
4296
|
+
const theme = useTuiTheme();
|
|
4297
|
+
return /* @__PURE__ */ jsx13(Text12, { color: theme.crit, children });
|
|
4298
|
+
}
|
|
3856
4299
|
|
|
3857
4300
|
// src/bootstrap-ink.tsx
|
|
3858
|
-
import { jsx as
|
|
4301
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
3859
4302
|
function enterAltScreen() {
|
|
3860
4303
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
3861
4304
|
}
|
|
@@ -3884,7 +4327,7 @@ async function bootstrapInk({ interval, config, daemon, mode }) {
|
|
|
3884
4327
|
if (altScreen) leaveAltScreen();
|
|
3885
4328
|
});
|
|
3886
4329
|
const { waitUntilExit } = render(
|
|
3887
|
-
/* @__PURE__ */
|
|
4330
|
+
/* @__PURE__ */ jsx14(MouseProvider, { children: /* @__PURE__ */ jsx14(
|
|
3888
4331
|
App,
|
|
3889
4332
|
{
|
|
3890
4333
|
interval,
|