letmecode 0.1.12 → 0.1.13
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/ink-app/dist/index.js +317 -136
- package/ink-app/dist/providers/antigravity.js +133 -558
- package/ink-app/dist/providers/index.js +1 -1
- package/package.json +11 -14
package/ink-app/dist/index.js
CHANGED
|
@@ -12,43 +12,38 @@ const ENABLE_MOUSE_TRACKING = `${ESC}[?1000h${ESC}[?1006h`;
|
|
|
12
12
|
const DISABLE_MOUSE_TRACKING = `${ESC}[?1006l${ESC}[?1000l`;
|
|
13
13
|
// SGR mouse report: ESC [ < button ; column ; row, ending in M (press) or m (release).
|
|
14
14
|
const SGR_MOUSE_SEQUENCE = new RegExp(`${ESC}\\[<(\\d+);(\\d+);(\\d+)([Mm])`, "g");
|
|
15
|
-
const
|
|
15
|
+
const DETAIL_TABS = [
|
|
16
16
|
{ id: "limit-windows", label: "Limits" },
|
|
17
17
|
{ id: "summary", label: "Summary" },
|
|
18
|
-
{ id: "day-to-day-analyses", label: "
|
|
19
|
-
{ id: "usage-by-model", label: "
|
|
18
|
+
{ id: "day-to-day-analyses", label: "Daily" },
|
|
19
|
+
{ id: "usage-by-model", label: "Models" }
|
|
20
20
|
];
|
|
21
21
|
const CODEX_CREDIT_COST_USD = 0.01;
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
cacheWrite: 11,
|
|
48
|
-
cacheWrite5m: 10,
|
|
49
|
-
cacheWrite1h: 10,
|
|
50
|
-
value: 10
|
|
51
|
-
};
|
|
22
|
+
const LIMIT_TABLE_COLUMNS = [
|
|
23
|
+
{ header: "Plan", width: 5 },
|
|
24
|
+
{ header: "Window", width: 6 },
|
|
25
|
+
{ header: "Used", width: 8 },
|
|
26
|
+
{ header: "Start", width: 14 },
|
|
27
|
+
{ header: "End", width: 14 },
|
|
28
|
+
{ header: "API eq.", width: 8 }
|
|
29
|
+
];
|
|
30
|
+
const DAILY_TABLE_COLUMNS = [
|
|
31
|
+
{ header: "Day", width: 9 },
|
|
32
|
+
{ header: "Ev", width: 5 },
|
|
33
|
+
{ header: "Input", width: 8 },
|
|
34
|
+
{ header: "Output", width: 7 },
|
|
35
|
+
{ header: "C read", width: 8 },
|
|
36
|
+
{ header: "C write", width: 7 },
|
|
37
|
+
{ header: "API eq.", width: 7 }
|
|
38
|
+
];
|
|
39
|
+
const MODEL_TABLE_COLUMNS = [
|
|
40
|
+
{ header: "Model", width: 16 },
|
|
41
|
+
{ header: "Input", width: 7 },
|
|
42
|
+
{ header: "Output", width: 7 },
|
|
43
|
+
{ header: "C read", width: 7 },
|
|
44
|
+
{ header: "C write", width: 7 },
|
|
45
|
+
{ header: "API eq.", width: 7 }
|
|
46
|
+
];
|
|
52
47
|
const COPILOT_ACTIONS = [
|
|
53
48
|
{ id: "vscode", label: "Start logging VS Code", enabled: true }
|
|
54
49
|
];
|
|
@@ -65,7 +60,7 @@ function App(props) {
|
|
|
65
60
|
const [providerStates, setProviderStates] = useState(providers.map((provider) => ({ provider, status: "loading" })));
|
|
66
61
|
const [selectedProviderId, setSelectedProviderId] = useState(providers[0]?.id ?? "");
|
|
67
62
|
const [hasUserSelectedProvider, setHasUserSelectedProvider] = useState(false);
|
|
68
|
-
const [
|
|
63
|
+
const [selectedDetailTabIndex, setSelectedDetailTabIndex] = useState(0);
|
|
69
64
|
const [selectedLimitRowIndex, setSelectedLimitRowIndex] = useState(0);
|
|
70
65
|
const [selectedDayRowIndex, setSelectedDayRowIndex] = useState(0);
|
|
71
66
|
const [selectedModelRowIndex, setSelectedModelRowIndex] = useState(0);
|
|
@@ -75,7 +70,7 @@ function App(props) {
|
|
|
75
70
|
const sortedProviderStates = React.useMemo(() => sortProviderStatesByUsage(providerStates), [providerStates]);
|
|
76
71
|
const selectedProviderIndex = Math.max(0, sortedProviderStates.findIndex((state) => state.provider.id === selectedProviderId));
|
|
77
72
|
const selectedProvider = sortedProviderStates[selectedProviderIndex];
|
|
78
|
-
const
|
|
73
|
+
const selectedDetailTab = DETAIL_TABS[selectedDetailTabIndex];
|
|
79
74
|
const limitRows = getLimitRows(selectedProvider);
|
|
80
75
|
const dayRows = getDayRows(selectedProvider);
|
|
81
76
|
const modelRows = getModelRows(selectedProvider);
|
|
@@ -145,9 +140,30 @@ function App(props) {
|
|
|
145
140
|
return;
|
|
146
141
|
}
|
|
147
142
|
if (regionId.startsWith("vtab:")) {
|
|
148
|
-
|
|
143
|
+
setSelectedDetailTabIndex(Number(regionId.slice("vtab:".length)));
|
|
149
144
|
}
|
|
150
145
|
});
|
|
146
|
+
const moveSelectedTableRow = useCallback((delta) => {
|
|
147
|
+
if (selectedDetailTab.id === "limit-windows") {
|
|
148
|
+
setSelectedLimitRowIndex(clampSelectionIndex(activeLimitRowIndex + delta, limitRows.length));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (selectedDetailTab.id === "usage-by-model") {
|
|
152
|
+
setSelectedModelRowIndex(clampSelectionIndex(activeModelRowIndex + delta, modelRows.length));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (selectedDetailTab.id === "day-to-day-analyses") {
|
|
156
|
+
setSelectedDayRowIndex(clampSelectionIndex(activeDayRowIndex + delta, dayRows.length));
|
|
157
|
+
}
|
|
158
|
+
}, [
|
|
159
|
+
activeDayRowIndex,
|
|
160
|
+
activeLimitRowIndex,
|
|
161
|
+
activeModelRowIndex,
|
|
162
|
+
dayRows.length,
|
|
163
|
+
limitRows.length,
|
|
164
|
+
modelRows.length,
|
|
165
|
+
selectedDetailTab.id
|
|
166
|
+
]);
|
|
151
167
|
useInput((input, key) => {
|
|
152
168
|
// Mouse reports arrive as SGR escape sequences and are handled by useMouseClick.
|
|
153
169
|
// Ink strips the leading ESC, leaving e.g. "[<0;10;5M" — never treat that as a key.
|
|
@@ -175,32 +191,12 @@ function App(props) {
|
|
|
175
191
|
return;
|
|
176
192
|
}
|
|
177
193
|
if (key.rightArrow) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (selectedVerticalTab.id === "usage-by-model") {
|
|
183
|
-
setSelectedModelRowIndex(clampSelectionIndex(activeModelRowIndex + 1, modelRows.length));
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
if (selectedVerticalTab.id === "day-to-day-analyses") {
|
|
187
|
-
setSelectedDayRowIndex(clampSelectionIndex(activeDayRowIndex + 1, dayRows.length));
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
194
|
+
setSelectedDetailTabIndex((current) => (current + 1) % DETAIL_TABS.length);
|
|
195
|
+
return;
|
|
190
196
|
}
|
|
191
197
|
if (key.leftArrow) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
if (selectedVerticalTab.id === "usage-by-model") {
|
|
197
|
-
setSelectedModelRowIndex(clampSelectionIndex(activeModelRowIndex - 1, modelRows.length));
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if (selectedVerticalTab.id === "day-to-day-analyses") {
|
|
201
|
-
setSelectedDayRowIndex(clampSelectionIndex(activeDayRowIndex - 1, dayRows.length));
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
198
|
+
setSelectedDetailTabIndex((current) => (current - 1 + DETAIL_TABS.length) % DETAIL_TABS.length);
|
|
199
|
+
return;
|
|
204
200
|
}
|
|
205
201
|
if ((key.tab && !key.shift) || input === "]") {
|
|
206
202
|
setSelectedProviderId(sortedProviderStates[(selectedProviderIndex + 1) % sortedProviderStates.length].provider.id);
|
|
@@ -214,14 +210,14 @@ function App(props) {
|
|
|
214
210
|
return;
|
|
215
211
|
}
|
|
216
212
|
if (key.downArrow || input === "j") {
|
|
217
|
-
|
|
213
|
+
moveSelectedTableRow(1);
|
|
218
214
|
return;
|
|
219
215
|
}
|
|
220
216
|
if (key.upArrow || input === "k") {
|
|
221
|
-
|
|
217
|
+
moveSelectedTableRow(-1);
|
|
222
218
|
}
|
|
223
219
|
});
|
|
224
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: viewportHeight, overflow: "hidden", children: [_jsx(Text, { bold: true, color: "cyan", children: "letmecode usage dashboard" }),
|
|
220
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: viewportHeight, overflow: "hidden", children: [_jsx(Text, { bold: true, color: "cyan", children: "letmecode usage dashboard" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "gray", children: "Provider " }), sortedProviderStates.map((state) => (_jsx(ProviderTab, { label: state.provider.label, active: state.provider.id === selectedProvider.provider.id, status: state.status, regionRef: getRegionRef(`provider:${state.provider.id}`) }, state.provider.id)))] }), _jsxs(Box, { children: [_jsx(Text, { color: "gray", children: "View " }), DETAIL_TABS.map((tab, index) => (_jsx(DetailTab, { label: tab.label, active: index === selectedDetailTabIndex, regionRef: getRegionRef(`vtab:${index}`) }, tab.id)))] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: [_jsx(Box, { flexGrow: 1, overflow: "hidden", children: _jsx(Box, { ref: contentPanelRef, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: _jsx(ContentPanel, { providerState: selectedProvider, tabId: selectedDetailTab.id, selectedLimitRowKey: selectedLimitRow ? getLimitRowKey(selectedLimitRow) : undefined, selectedDayKey: selectedDayRow?.dayKey, selectedModelId: selectedModelRow?.modelId, availableHeight: contentPanelHeight }) }) }), _jsx(SelectionDetailsPanel, { providerState: selectedProvider, tabId: selectedDetailTab.id, selectedLimitRow: selectedLimitRow, selectedDayRow: selectedDayRow, selectedModelRow: selectedModelRow }), _jsx(CopilotActionsPanel, { providerState: selectedProvider, actionMessage: copilotActionMessage, selectedActionIndex: selectedCopilotActionIndex }), selectedProvider.status === "ready" && selectedProvider.stats.warnings.length > 0 ? (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, flexDirection: "column", overflow: "hidden", children: [_jsx(Text, { color: "yellow", children: "Warnings" }), selectedProvider.stats.warnings.map((warning) => (_jsx(Text, { children: warning }, warning)))] })) : null] }), _jsx(Text, { color: "gray", children: "Tab provider \u00B7 \u2190/\u2192 view \u00B7 \u2191/\u2193 row \u00B7 q quit" })] }));
|
|
225
221
|
}
|
|
226
222
|
function CopilotActionsPanel(props) {
|
|
227
223
|
if (props.providerState.provider.id !== "copilot") {
|
|
@@ -257,16 +253,23 @@ function formatCopilotLoggingResult(result) {
|
|
|
257
253
|
}
|
|
258
254
|
function ProviderTab(props) {
|
|
259
255
|
const statusColor = props.status === "error" ? "red" : props.status === "loading" ? "yellow" : "green";
|
|
260
|
-
const tabLabel = props.active ? `
|
|
261
|
-
return (_jsx(Box, { marginRight:
|
|
256
|
+
const tabLabel = props.active ? `[${props.label}]` : ` ${props.label} `;
|
|
257
|
+
return (_jsx(Box, { marginRight: 2, ref: props.regionRef, children: _jsx(Text, { color: statusColor, bold: props.active, children: tabLabel }) }));
|
|
262
258
|
}
|
|
263
|
-
function
|
|
264
|
-
|
|
259
|
+
function DetailTab(props) {
|
|
260
|
+
const tabLabel = props.active ? `[${props.label}]` : ` ${props.label} `;
|
|
261
|
+
return (_jsx(Box, { marginRight: 2, ref: props.regionRef, children: _jsx(Text, { wrap: "truncate-end", bold: props.active, children: tabLabel }) }));
|
|
265
262
|
}
|
|
266
263
|
function SummaryPanel(props) {
|
|
267
264
|
const { summary } = props.stats;
|
|
268
|
-
const
|
|
269
|
-
|
|
265
|
+
const totals = summary.totals;
|
|
266
|
+
const period = resolveSummaryPeriod(props.stats.dayUsage);
|
|
267
|
+
const cacheRatio = resolveCacheRatio(totals);
|
|
268
|
+
const averageTokensPerEvent = totals.eventCount > 0 ? totals.totalTokens / totals.eventCount : 0;
|
|
269
|
+
const costPerEvent = totals.eventCount > 0
|
|
270
|
+
? (totals.estimatedCredits * CODEX_CREDIT_COST_USD) / totals.eventCount
|
|
271
|
+
: 0;
|
|
272
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [props.stats.providerLabel, " overview"] }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsxs(Box, { flexDirection: "column", width: 45, children: [_jsx(DetailRow, { label: "Plan", value: summary.distinctPlanTypes.join(", ") || "none" }), _jsx(DetailRow, { label: "Models", value: summary.distinctModels.join(", ") || "none" }), _jsx(DetailRow, { label: "Period", value: period }), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", children: "Usage" }), _jsx(DetailRow, { label: "Events", value: formatInteger(totals.eventCount) }), _jsx(DetailRow, { label: "Input", value: formatOverviewTokenCount(totals.inputTokens) }), _jsx(DetailRow, { label: "Output", value: formatOverviewTokenCount(totals.outputTokens) }), _jsx(DetailRow, { label: "Cache read", value: formatCacheOverviewTokenCount(totals, totals.cacheReadInputTokens) }), _jsx(DetailRow, { label: "Reasoning", value: formatOverviewTokenCount(totals.reasoningOutputTokens) }), _jsx(DetailRow, { label: "Total", value: formatOverviewTokenCount(totals.totalTokens) }), _jsx(DetailRow, { label: "API equiv.", value: formatUsageUsd(totals) })] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "cyan", children: "Efficiency" }), _jsx(DetailRow, { label: "Cache ratio", value: formatPercent(cacheRatio) }), _jsx(DetailRow, { label: "Input/output", value: formatInputOutputRatio(totals) }), _jsx(DetailRow, { label: "Avg/event", value: `${formatOverviewTokenCount(averageTokensPerEvent)} tokens` }), _jsx(DetailRow, { label: "Cost/event", value: formatUnitUsd(costPerEvent) }), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", children: "Data source" }), _jsx(DetailRow, { label: "Files", value: formatInteger(summary.filesScanned) }), _jsx(DetailRow, { label: "Lines", value: formatInteger(summary.linesRead) }), _jsx(DetailRow, { label: "Path", value: summary.rootPath })] })] })] }));
|
|
270
273
|
}
|
|
271
274
|
function ContentPanel(props) {
|
|
272
275
|
if (props.providerState.status === "loading") {
|
|
@@ -287,62 +290,27 @@ function ContentPanel(props) {
|
|
|
287
290
|
return (_jsx(UsageByModelPanel, { stats: props.providerState.stats, selectedModelId: props.selectedModelId, availableHeight: props.availableHeight }));
|
|
288
291
|
}
|
|
289
292
|
function LimitWindowsPanel(props) {
|
|
290
|
-
const hasPrimaryWindows = props.stats.primaryLimitWindows.length > 0;
|
|
291
293
|
const bodyLines = [
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
...(
|
|
295
|
-
? [{ key: "section-separator", text: buildLimitSectionSeparatorLine(), color: "gray" }]
|
|
296
|
-
: [{ key: "section-gap", text: "" }]),
|
|
297
|
-
{ key: "secondary-title", text: "Secondary Limit Windows", bold: true },
|
|
298
|
-
...buildLimitWindowSectionLines("secondary", props.stats.secondaryLimitWindows, props.selectedRowKey)
|
|
294
|
+
...buildLimitWindowTableLines("primary", "Primary limits", props.stats.primaryLimitWindows, props.selectedRowKey),
|
|
295
|
+
{ key: "section-gap", text: "" },
|
|
296
|
+
...buildLimitWindowTableLines("secondary", "Secondary limits", props.stats.secondaryLimitWindows, props.selectedRowKey)
|
|
299
297
|
];
|
|
300
|
-
return (_jsx(ScrollableLineViewport, { bodyLines: bodyLines,
|
|
298
|
+
return (_jsx(ScrollableLineViewport, { bodyLines: bodyLines, availableHeight: props.availableHeight }));
|
|
301
299
|
}
|
|
302
300
|
function UsageByModelPanel(props) {
|
|
303
301
|
if (props.stats.modelUsage.length === 0) {
|
|
304
302
|
return _jsx(Text, { color: "gray", children: "No model usage found." });
|
|
305
303
|
}
|
|
306
304
|
const totals = props.stats.summary.totals;
|
|
307
|
-
const
|
|
308
|
-
return (_jsx(ScrollableLineViewport, {
|
|
309
|
-
{
|
|
310
|
-
key: "model-header",
|
|
311
|
-
text: isCodexProvider
|
|
312
|
-
? `${pad("model", MODEL_USAGE_COLUMNS.model)} ${pad("input", MODEL_USAGE_COLUMNS.input)} ${pad("output", MODEL_USAGE_COLUMNS.output)} ${pad("cacheRead", MODEL_USAGE_COLUMNS.cacheRead)} ${pad("cacheWrite", MODEL_USAGE_COLUMNS.cacheWrite)} ${pad("cacheW5m", MODEL_USAGE_COLUMNS.cacheWrite5m)} ${pad("cacheW1h", MODEL_USAGE_COLUMNS.cacheWrite1h)} value`
|
|
313
|
-
: `${pad("model", MODEL_USAGE_COLUMNS.model)} ${pad("input", MODEL_USAGE_COLUMNS.input)} ${pad("output", MODEL_USAGE_COLUMNS.output)} ${pad("cacheRead", MODEL_USAGE_COLUMNS.cacheRead)} ${pad("cacheWrite", MODEL_USAGE_COLUMNS.cacheWrite)} ${pad("cacheW5m", MODEL_USAGE_COLUMNS.cacheWrite5m)} ${pad("cacheW1h", MODEL_USAGE_COLUMNS.cacheWrite1h)} ${pad("credits", MODEL_USAGE_COLUMNS.credits)} value`,
|
|
314
|
-
color: "gray"
|
|
315
|
-
}
|
|
316
|
-
], bodyLines: props.stats.modelUsage.map((row) => ({
|
|
317
|
-
key: `model-row:${row.modelId}`,
|
|
318
|
-
text: formatModelUsageRow(row.modelId, row.totals, isCodexProvider),
|
|
319
|
-
inverse: props.selectedModelId === row.modelId,
|
|
320
|
-
color: props.selectedModelId === row.modelId ? "cyan" : undefined
|
|
321
|
-
})), footerLines: [
|
|
322
|
-
{
|
|
323
|
-
key: "model-total",
|
|
324
|
-
text: formatModelUsageRow("TOTAL", totals, isCodexProvider),
|
|
325
|
-
color: "cyan"
|
|
326
|
-
}
|
|
327
|
-
], selectedBodyLineKey: props.selectedModelId ? `model-row:${props.selectedModelId}` : undefined, availableHeight: props.availableHeight }));
|
|
305
|
+
const bodyLines = buildModelUsageTableLines(props.stats.modelUsage, totals, props.selectedModelId);
|
|
306
|
+
return (_jsx(ScrollableLineViewport, { bodyLines: bodyLines, selectedBodyLineKey: props.selectedModelId ? `model-row:${props.selectedModelId}` : undefined, availableHeight: props.availableHeight }));
|
|
328
307
|
}
|
|
329
308
|
function DayToDayPanel(props) {
|
|
330
309
|
if (props.stats.dayUsage.length === 0) {
|
|
331
310
|
return _jsx(Text, { color: "gray", children: "No day-by-day usage found." });
|
|
332
311
|
}
|
|
333
|
-
const
|
|
334
|
-
return (_jsx(ScrollableLineViewport, {
|
|
335
|
-
{
|
|
336
|
-
key: "day-header",
|
|
337
|
-
text: `${pad("day", DAY_USAGE_COLUMNS.day)} ${pad("events", DAY_USAGE_COLUMNS.events)} ${pad("input", DAY_USAGE_COLUMNS.input)} ${pad("output", DAY_USAGE_COLUMNS.output)} ${pad("cacheRead", DAY_USAGE_COLUMNS.cacheRead)} ${pad("cacheWrite", DAY_USAGE_COLUMNS.cacheWrite)} ${pad("cacheW5m", DAY_USAGE_COLUMNS.cacheWrite5m)} ${pad("cacheW1h", DAY_USAGE_COLUMNS.cacheWrite1h)} value`,
|
|
338
|
-
color: "gray"
|
|
339
|
-
}
|
|
340
|
-
], bodyLines: props.stats.dayUsage.map((row) => ({
|
|
341
|
-
key: `day-row:${row.dayKey}`,
|
|
342
|
-
text: `${pad(formatUtcDay(row.dayKey), DAY_USAGE_COLUMNS.day)} ${pad(formatCompactTokenCount(row.totals.eventCount), DAY_USAGE_COLUMNS.events)} ${pad(formatCompactTokenCount(row.totals.inputTokens), DAY_USAGE_COLUMNS.input)} ${pad(formatCompactTokenCount(row.totals.outputTokens), DAY_USAGE_COLUMNS.output)} ${pad(formatCompactCacheTokens(row.totals, row.totals.cacheReadInputTokens), DAY_USAGE_COLUMNS.cacheRead)} ${pad(formatCompactCacheTokens(row.totals, row.totals.cacheWriteInputTokens), DAY_USAGE_COLUMNS.cacheWrite)} ${pad(formatOptionalCompactTokens(row.totals.cacheWrite5mInputTokens), DAY_USAGE_COLUMNS.cacheWrite5m)} ${pad(formatOptionalCompactTokens(row.totals.cacheWrite1hInputTokens), DAY_USAGE_COLUMNS.cacheWrite1h)} ${pad(formatUsageUsd(row.totals), DAY_USAGE_COLUMNS.value)}`,
|
|
343
|
-
inverse: props.selectedDayKey === row.dayKey,
|
|
344
|
-
color: props.selectedDayKey === row.dayKey ? "cyan" : undefined
|
|
345
|
-
})), selectedBodyLineKey: props.selectedDayKey ? `day-row:${props.selectedDayKey}` : undefined, availableHeight: props.availableHeight }));
|
|
312
|
+
const bodyLines = buildDailyUsageTableLines(props.stats.dayUsage, props.selectedDayKey);
|
|
313
|
+
return (_jsx(ScrollableLineViewport, { bodyLines: bodyLines, selectedBodyLineKey: props.selectedDayKey ? `day-row:${props.selectedDayKey}` : undefined, availableHeight: props.availableHeight }));
|
|
346
314
|
}
|
|
347
315
|
function ScrollableLineViewport(props) {
|
|
348
316
|
const headerLines = props.headerLines ?? [];
|
|
@@ -362,28 +330,146 @@ function ScrollableLineViewport(props) {
|
|
|
362
330
|
function ScrollableViewportLine(props) {
|
|
363
331
|
return (_jsx(Text, { bold: props.line.bold, color: props.line.color, inverse: props.line.inverse, wrap: "truncate-end", children: props.line.text }));
|
|
364
332
|
}
|
|
365
|
-
function
|
|
333
|
+
function buildTableBorder(columns, left, middle, right) {
|
|
334
|
+
return `${left}${columns.map((column) => "─".repeat(column.width + 2)).join(middle)}${right}`;
|
|
335
|
+
}
|
|
336
|
+
function buildTableRow(columns, cells) {
|
|
337
|
+
return `│${columns.map((column, index) => ` ${pad(cells[index] ?? "", column.width)} `).join("│")}│`;
|
|
338
|
+
}
|
|
339
|
+
function buildLimitWindowTableLines(scope, title, windows, selectedRowKey) {
|
|
366
340
|
if (windows.length === 0) {
|
|
367
|
-
return [
|
|
341
|
+
return [
|
|
342
|
+
{ key: `${scope}-title`, text: title, bold: true },
|
|
343
|
+
{ key: `${scope}-empty`, text: "No windows found.", color: "gray" }
|
|
344
|
+
];
|
|
368
345
|
}
|
|
369
346
|
return [
|
|
347
|
+
{ key: `${scope}-title`, text: title, bold: true },
|
|
348
|
+
{
|
|
349
|
+
key: `${scope}-top-border`,
|
|
350
|
+
text: buildTableBorder(LIMIT_TABLE_COLUMNS, "┌", "┬", "┐"),
|
|
351
|
+
color: "gray"
|
|
352
|
+
},
|
|
370
353
|
{
|
|
371
354
|
key: `${scope}-header`,
|
|
372
|
-
text:
|
|
355
|
+
text: buildTableRow(LIMIT_TABLE_COLUMNS, LIMIT_TABLE_COLUMNS.map((column) => column.header)),
|
|
356
|
+
color: "gray"
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
key: `${scope}-header-border`,
|
|
360
|
+
text: buildTableBorder(LIMIT_TABLE_COLUMNS, "├", "┼", "┤"),
|
|
373
361
|
color: "gray"
|
|
374
362
|
},
|
|
375
363
|
...windows.map((window) => {
|
|
376
364
|
const lineKey = getLimitRowKey(window);
|
|
377
|
-
const windowLabel =
|
|
365
|
+
const windowLabel = formatCompactWindowMinutes(window.windowMinutes);
|
|
378
366
|
const usedLabel = formatUsedPercentRange(window.minUsedPercent, window.maxUsedPercent);
|
|
379
367
|
const isSelected = selectedRowKey === lineKey;
|
|
380
368
|
return {
|
|
381
369
|
key: `limit-row:${lineKey}`,
|
|
382
|
-
text:
|
|
370
|
+
text: buildTableRow(LIMIT_TABLE_COLUMNS, [
|
|
371
|
+
window.planType,
|
|
372
|
+
windowLabel,
|
|
373
|
+
usedLabel,
|
|
374
|
+
formatCompactLocalDateTime(window.startTimeUtcIso),
|
|
375
|
+
formatCompactLocalDateTime(window.endTimeUtcIso),
|
|
376
|
+
formatUsd(window.totals.estimatedCredits * CODEX_CREDIT_COST_USD)
|
|
377
|
+
]),
|
|
378
|
+
inverse: isSelected,
|
|
379
|
+
color: isSelected ? "cyan" : undefined
|
|
380
|
+
};
|
|
381
|
+
}),
|
|
382
|
+
{
|
|
383
|
+
key: `${scope}-bottom-border`,
|
|
384
|
+
text: buildTableBorder(LIMIT_TABLE_COLUMNS, "└", "┴", "┘"),
|
|
385
|
+
color: "gray"
|
|
386
|
+
}
|
|
387
|
+
];
|
|
388
|
+
}
|
|
389
|
+
function buildDailyUsageTableLines(rows, selectedDayKey) {
|
|
390
|
+
return [
|
|
391
|
+
{ key: "daily-title", text: "Daily usage", bold: true },
|
|
392
|
+
{
|
|
393
|
+
key: "daily-top-border",
|
|
394
|
+
text: buildTableBorder(DAILY_TABLE_COLUMNS, "┌", "┬", "┐"),
|
|
395
|
+
color: "gray"
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
key: "daily-header",
|
|
399
|
+
text: buildTableRow(DAILY_TABLE_COLUMNS, DAILY_TABLE_COLUMNS.map((column) => column.header)),
|
|
400
|
+
color: "gray"
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
key: "daily-header-border",
|
|
404
|
+
text: buildTableBorder(DAILY_TABLE_COLUMNS, "├", "┼", "┤"),
|
|
405
|
+
color: "gray"
|
|
406
|
+
},
|
|
407
|
+
...rows.map((row) => {
|
|
408
|
+
const isSelected = selectedDayKey === row.dayKey;
|
|
409
|
+
return {
|
|
410
|
+
key: `day-row:${row.dayKey}`,
|
|
411
|
+
text: buildTableRow(DAILY_TABLE_COLUMNS, [
|
|
412
|
+
formatUtcDay(row.dayKey),
|
|
413
|
+
formatCompactTokenCount(row.totals.eventCount),
|
|
414
|
+
formatCompactTokenCount(row.totals.inputTokens),
|
|
415
|
+
formatCompactTokenCount(row.totals.outputTokens),
|
|
416
|
+
formatCompactCacheTokens(row.totals, row.totals.cacheReadInputTokens),
|
|
417
|
+
formatCompactCacheTokens(row.totals, row.totals.cacheWriteInputTokens),
|
|
418
|
+
formatUsageUsd(row.totals)
|
|
419
|
+
]),
|
|
383
420
|
inverse: isSelected,
|
|
384
421
|
color: isSelected ? "cyan" : undefined
|
|
385
422
|
};
|
|
386
|
-
})
|
|
423
|
+
}),
|
|
424
|
+
{
|
|
425
|
+
key: "daily-bottom-border",
|
|
426
|
+
text: buildTableBorder(DAILY_TABLE_COLUMNS, "└", "┴", "┘"),
|
|
427
|
+
color: "gray"
|
|
428
|
+
}
|
|
429
|
+
];
|
|
430
|
+
}
|
|
431
|
+
function buildModelUsageTableLines(rows, totals, selectedModelId) {
|
|
432
|
+
return [
|
|
433
|
+
{ key: "model-title", text: "Model usage", bold: true },
|
|
434
|
+
{
|
|
435
|
+
key: "model-top-border",
|
|
436
|
+
text: buildTableBorder(MODEL_TABLE_COLUMNS, "┌", "┬", "┐"),
|
|
437
|
+
color: "gray"
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
key: "model-header",
|
|
441
|
+
text: buildTableRow(MODEL_TABLE_COLUMNS, MODEL_TABLE_COLUMNS.map((column) => column.header)),
|
|
442
|
+
color: "gray"
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
key: "model-header-border",
|
|
446
|
+
text: buildTableBorder(MODEL_TABLE_COLUMNS, "├", "┼", "┤"),
|
|
447
|
+
color: "gray"
|
|
448
|
+
},
|
|
449
|
+
...rows.map((row) => {
|
|
450
|
+
const isSelected = selectedModelId === row.modelId;
|
|
451
|
+
return {
|
|
452
|
+
key: `model-row:${row.modelId}`,
|
|
453
|
+
text: formatModelUsageTableRow(row.modelId, row.totals),
|
|
454
|
+
inverse: isSelected,
|
|
455
|
+
color: isSelected ? "cyan" : undefined
|
|
456
|
+
};
|
|
457
|
+
}),
|
|
458
|
+
{
|
|
459
|
+
key: "model-total-border",
|
|
460
|
+
text: buildTableBorder(MODEL_TABLE_COLUMNS, "├", "┼", "┤"),
|
|
461
|
+
color: "gray"
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
key: "model-total",
|
|
465
|
+
text: formatModelUsageTableRow("TOTAL", totals),
|
|
466
|
+
color: "cyan"
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
key: "model-bottom-border",
|
|
470
|
+
text: buildTableBorder(MODEL_TABLE_COLUMNS, "└", "┴", "┘"),
|
|
471
|
+
color: "gray"
|
|
472
|
+
}
|
|
387
473
|
];
|
|
388
474
|
}
|
|
389
475
|
function SelectionDetailsPanel(props) {
|
|
@@ -392,17 +478,23 @@ function SelectionDetailsPanel(props) {
|
|
|
392
478
|
}
|
|
393
479
|
if (props.tabId === "limit-windows" && props.selectedLimitRow) {
|
|
394
480
|
const row = props.selectedLimitRow;
|
|
395
|
-
return (_jsxs(Box, {
|
|
481
|
+
return (_jsx(DetailsPanelFrame, { children: _jsxs(Box, { children: [_jsxs(Box, { flexDirection: "column", width: 25, children: [_jsx(DetailRow, { label: "Plan", value: row.planType }), _jsx(DetailRow, { label: "Window", value: formatCompactWindowMinutes(row.windowMinutes) }), _jsx(DetailRow, { label: "Usage", value: formatUsedPercentRange(row.minUsedPercent, row.maxUsedPercent) }), _jsx(DetailRow, { label: "Events", value: formatInteger(row.eventCount) }), _jsx(DetailRow, { label: "API eq.", value: formatUsageUsd(row.totals) })] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailRow, { label: "Period", value: `${formatCompactLocalDateTime(row.startTimeUtcIso)} → ${formatCompactLocalDateTime(row.endTimeUtcIso)}` }), _jsx(DetailRow, { label: "Input", value: formatInteger(row.totals.inputTokens) }), _jsx(DetailRow, { label: "Cache read", value: formatCacheTokens(row.totals, row.totals.cacheReadInputTokens) }), _jsx(DetailRow, { label: "Cache write", value: formatCacheTokens(row.totals, row.totals.cacheWriteInputTokens) }), _jsx(DetailRow, { label: "Output", value: formatInteger(row.totals.outputTokens) }), _jsx(DetailRow, { label: "Total", value: formatInteger(row.totals.totalTokens) })] })] }) }));
|
|
396
482
|
}
|
|
397
483
|
if (props.tabId === "day-to-day-analyses" && props.selectedDayRow) {
|
|
398
484
|
const row = props.selectedDayRow;
|
|
399
|
-
return (_jsxs(
|
|
485
|
+
return (_jsxs(DetailsPanelFrame, { children: [_jsxs(Text, { children: ["day: ", formatUtcDay(row.dayKey), " events: ", formatInteger(row.totals.eventCount), " models: ", formatInteger(row.distinctModels.length), " plans: ", formatInteger(row.distinctPlanTypes.length)] }), _jsxs(Text, { children: ["range: ", formatEventRange(row.firstEventUtcIso, row.lastEventUtcIso)] }), _jsxs(Text, { children: ["models: ", row.distinctModels.join(", ") || "none"] }), _jsxs(Text, { children: ["plans: ", row.distinctPlanTypes.join(", ") || "none"] }), _jsx(UsageTotalsDetails, { totals: row.totals })] }));
|
|
400
486
|
}
|
|
401
487
|
if (props.tabId === "usage-by-model" && props.selectedModelRow) {
|
|
402
|
-
return (_jsxs(
|
|
488
|
+
return (_jsxs(DetailsPanelFrame, { children: [_jsxs(Text, { children: ["model: ", props.selectedModelRow.modelId, " events: ", formatInteger(props.selectedModelRow.totals.eventCount)] }), _jsx(UsageTotalsDetails, { totals: props.selectedModelRow.totals, modelId: props.selectedModelRow.modelId })] }));
|
|
403
489
|
}
|
|
404
490
|
return null;
|
|
405
491
|
}
|
|
492
|
+
function DetailsPanelFrame(props) {
|
|
493
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: "cyan", children: "Details" }), props.children] }));
|
|
494
|
+
}
|
|
495
|
+
function DetailRow(props) {
|
|
496
|
+
return (_jsxs(Text, { children: [pad(props.label, 14), props.value] }));
|
|
497
|
+
}
|
|
406
498
|
function UsageTotalsDetails(props) {
|
|
407
499
|
const { totals } = props;
|
|
408
500
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(UsageBreakdownLines, { totals: totals }), _jsxs(Text, { children: ["Burned tokens for: ", formatUsageUsd(totals, props.modelId)] }), _jsxs(Text, { children: ["IpO: ", formatInputPerOutput(totals)] })] }));
|
|
@@ -410,6 +502,28 @@ function UsageTotalsDetails(props) {
|
|
|
410
502
|
function formatInteger(value) {
|
|
411
503
|
return Math.round(value).toLocaleString("en-US");
|
|
412
504
|
}
|
|
505
|
+
function formatOverviewTokenCount(value) {
|
|
506
|
+
const roundedValue = Math.round(value);
|
|
507
|
+
if (roundedValue >= 1000000000) {
|
|
508
|
+
return `${formatFixedCompactNumber(roundedValue / 1000000000)}B`;
|
|
509
|
+
}
|
|
510
|
+
if (roundedValue >= 1000000) {
|
|
511
|
+
return `${formatFixedCompactNumber(roundedValue / 1000000)}M`;
|
|
512
|
+
}
|
|
513
|
+
if (roundedValue >= 1000) {
|
|
514
|
+
return `${formatFixedCompactNumber(roundedValue / 1000)}K`;
|
|
515
|
+
}
|
|
516
|
+
return formatInteger(roundedValue);
|
|
517
|
+
}
|
|
518
|
+
function formatCacheOverviewTokenCount(totals, value) {
|
|
519
|
+
return totals.cacheStatus === "unavailable" ? "-" : formatOverviewTokenCount(value);
|
|
520
|
+
}
|
|
521
|
+
function formatFixedCompactNumber(value) {
|
|
522
|
+
return value.toLocaleString("en-US", {
|
|
523
|
+
maximumFractionDigits: value < 10 ? 2 : 1,
|
|
524
|
+
minimumFractionDigits: 0
|
|
525
|
+
});
|
|
526
|
+
}
|
|
413
527
|
function formatCompactTokenCount(value) {
|
|
414
528
|
const roundedValue = Math.round(value);
|
|
415
529
|
if (roundedValue < 1000) {
|
|
@@ -465,18 +579,51 @@ function formatUsd(value) {
|
|
|
465
579
|
maximumFractionDigits: 2
|
|
466
580
|
});
|
|
467
581
|
}
|
|
582
|
+
function formatUnitUsd(value) {
|
|
583
|
+
if (!Number.isFinite(value)) {
|
|
584
|
+
return "-";
|
|
585
|
+
}
|
|
586
|
+
return value.toLocaleString("en-US", {
|
|
587
|
+
currency: "USD",
|
|
588
|
+
style: "currency",
|
|
589
|
+
minimumFractionDigits: value < 0.01 && value > 0 ? 4 : 3,
|
|
590
|
+
maximumFractionDigits: value < 0.01 && value > 0 ? 4 : 3
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
function formatPercent(value) {
|
|
594
|
+
if (!Number.isFinite(value)) {
|
|
595
|
+
return "-";
|
|
596
|
+
}
|
|
597
|
+
return `${value.toLocaleString("en-US", {
|
|
598
|
+
maximumFractionDigits: 1,
|
|
599
|
+
minimumFractionDigits: 0
|
|
600
|
+
})}%`;
|
|
601
|
+
}
|
|
602
|
+
function resolveCacheRatio(totals) {
|
|
603
|
+
if (totals.cacheStatus === "unavailable") {
|
|
604
|
+
return NaN;
|
|
605
|
+
}
|
|
606
|
+
const inputPool = totals.inputTokens +
|
|
607
|
+
totals.cacheReadInputTokens +
|
|
608
|
+
totals.cacheWriteInputTokens;
|
|
609
|
+
return inputPool > 0
|
|
610
|
+
? (totals.cacheReadInputTokens / inputPool) * 100
|
|
611
|
+
: 0;
|
|
612
|
+
}
|
|
613
|
+
function formatInputOutputRatio(totals) {
|
|
614
|
+
if (totals.outputTokens <= 0) {
|
|
615
|
+
return "-";
|
|
616
|
+
}
|
|
617
|
+
return `${(totals.inputTokens / totals.outputTokens).toLocaleString("en-US", {
|
|
618
|
+
maximumFractionDigits: 1,
|
|
619
|
+
minimumFractionDigits: 0
|
|
620
|
+
})} : 1`;
|
|
621
|
+
}
|
|
468
622
|
function formatUsedPercentRange(minUsedPercent, maxUsedPercent) {
|
|
623
|
+
const fmt = (v) => `${Math.round(v)}%`;
|
|
469
624
|
return minUsedPercent === maxUsedPercent
|
|
470
|
-
?
|
|
471
|
-
: `${minUsedPercent}
|
|
472
|
-
}
|
|
473
|
-
function buildLimitSectionSeparatorLine() {
|
|
474
|
-
return "─".repeat(LIMIT_WINDOW_COLUMNS.plan +
|
|
475
|
-
LIMIT_WINDOW_COLUMNS.window +
|
|
476
|
-
LIMIT_WINDOW_COLUMNS.used +
|
|
477
|
-
LIMIT_WINDOW_COLUMNS.date * 2 +
|
|
478
|
-
LIMIT_WINDOW_COLUMNS.value +
|
|
479
|
-
5);
|
|
625
|
+
? fmt(minUsedPercent)
|
|
626
|
+
: `${fmt(minUsedPercent)}–${fmt(maxUsedPercent)}`;
|
|
480
627
|
}
|
|
481
628
|
function formatWindowMinutes(value) {
|
|
482
629
|
const hours = value / 60;
|
|
@@ -485,6 +632,13 @@ function formatWindowMinutes(value) {
|
|
|
485
632
|
}
|
|
486
633
|
return `${hours.toFixed(2)}h`;
|
|
487
634
|
}
|
|
635
|
+
function formatCompactWindowMinutes(value) {
|
|
636
|
+
const hours = value / 60;
|
|
637
|
+
if (hours >= 24) {
|
|
638
|
+
return `${formatCompactNumber(hours / 24)}d`;
|
|
639
|
+
}
|
|
640
|
+
return `${formatCompactNumber(hours)}h`;
|
|
641
|
+
}
|
|
488
642
|
function formatLocalDateTime(value) {
|
|
489
643
|
const parts = new Intl.DateTimeFormat("en-US", {
|
|
490
644
|
month: "short",
|
|
@@ -498,6 +652,29 @@ function formatLocalDateTime(value) {
|
|
|
498
652
|
const lookup = Object.fromEntries(parts.map((part) => [part.type, part.value]));
|
|
499
653
|
return `${lookup.day} ${lookup.month} ${lookup.year} ${lookup.hour}:${lookup.minute}`;
|
|
500
654
|
}
|
|
655
|
+
function formatCompactLocalDateTime(value) {
|
|
656
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
657
|
+
month: "short",
|
|
658
|
+
day: "2-digit",
|
|
659
|
+
hour: "2-digit",
|
|
660
|
+
minute: "2-digit",
|
|
661
|
+
hour12: false,
|
|
662
|
+
hourCycle: "h23"
|
|
663
|
+
}).formatToParts(new Date(value));
|
|
664
|
+
const lookup = Object.fromEntries(parts.map((part) => [part.type, part.value]));
|
|
665
|
+
return `${lookup.day} ${lookup.month} ${lookup.hour}:${lookup.minute}`;
|
|
666
|
+
}
|
|
667
|
+
function resolveSummaryPeriod(dayUsage) {
|
|
668
|
+
const timestamps = dayUsage.flatMap((row) => [
|
|
669
|
+
row.firstEventUtcIso,
|
|
670
|
+
row.lastEventUtcIso
|
|
671
|
+
]).filter((value) => Boolean(value));
|
|
672
|
+
if (timestamps.length === 0) {
|
|
673
|
+
return "-";
|
|
674
|
+
}
|
|
675
|
+
const sortedTimestamps = timestamps.sort();
|
|
676
|
+
return `${formatCompactLocalDateTime(sortedTimestamps[0])} → ${formatCompactLocalDateTime(sortedTimestamps[sortedTimestamps.length - 1])}`;
|
|
677
|
+
}
|
|
501
678
|
function formatUtcDay(value) {
|
|
502
679
|
if (value === "unknown") {
|
|
503
680
|
return "-";
|
|
@@ -520,12 +697,16 @@ function formatEventRange(firstEventUtcIso, lastEventUtcIso) {
|
|
|
520
697
|
function pad(value, length) {
|
|
521
698
|
return value.length >= length ? value.slice(0, length) : value.padEnd(length);
|
|
522
699
|
}
|
|
523
|
-
function
|
|
700
|
+
function formatModelUsageTableRow(modelId, totals) {
|
|
524
701
|
const displayModelId = modelId === "unknown" ? "-" : modelId;
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
702
|
+
return buildTableRow(MODEL_TABLE_COLUMNS, [
|
|
703
|
+
displayModelId,
|
|
704
|
+
formatCompactTokenCount(totals.inputTokens),
|
|
705
|
+
formatCompactTokenCount(totals.outputTokens),
|
|
706
|
+
formatCompactCacheTokens(totals, totals.cacheReadInputTokens),
|
|
707
|
+
formatCompactCacheTokens(totals, totals.cacheWriteInputTokens),
|
|
708
|
+
formatUsageUsd(totals, modelId)
|
|
709
|
+
]);
|
|
529
710
|
}
|
|
530
711
|
function UsageBreakdownLines(props) {
|
|
531
712
|
const { totals } = props;
|