letmecode 0.1.24 → 0.1.25
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/cli-options.js +6 -6
- package/ink-app/dist/index.js +137 -80
- package/package.json +1 -1
|
@@ -58,12 +58,12 @@ export function buildHelpText() {
|
|
|
58
58
|
" --no-usage Disable anonymous usage reporting",
|
|
59
59
|
"",
|
|
60
60
|
"Controls:",
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
" 1,
|
|
61
|
+
" Up / Down Select Provider, View, or table rows",
|
|
62
|
+
" Left / Right Change the selected Provider or View",
|
|
63
|
+
" Left / Right Do nothing when table rows are selected",
|
|
64
|
+
" Tab / Shift+Tab Switch providers without leaving the table",
|
|
65
|
+
" [ / ] Switch views without leaving the table",
|
|
66
|
+
" 1, Enter Run Copilot setup actions",
|
|
67
67
|
" q or Esc Quit",
|
|
68
68
|
"",
|
|
69
69
|
"Trace logging:",
|
package/ink-app/dist/index.js
CHANGED
|
@@ -7,18 +7,19 @@ import { reportAnonymousUsage } from "./reporting.js";
|
|
|
7
7
|
import { estimateLimitFullValue } from "./providers/limits.js";
|
|
8
8
|
const ESC = String.fromCharCode(0x1b);
|
|
9
9
|
// Normal mouse tracking (button press/release only) + SGR extended coordinates.
|
|
10
|
-
// This makes the
|
|
10
|
+
// This makes the controls clickable while leaving Shift+drag native text selection
|
|
11
11
|
// available everywhere else, exactly like Midnight Commander.
|
|
12
12
|
const ENABLE_MOUSE_TRACKING = `${ESC}[?1000h${ESC}[?1006h`;
|
|
13
13
|
const DISABLE_MOUSE_TRACKING = `${ESC}[?1006l${ESC}[?1000l`;
|
|
14
14
|
// SGR mouse report: ESC [ < button ; column ; row, ending in M (press) or m (release).
|
|
15
15
|
const SGR_MOUSE_SEQUENCE = new RegExp(`${ESC}\\[<(\\d+);(\\d+);(\\d+)([Mm])`, "g");
|
|
16
|
-
const
|
|
16
|
+
const DETAIL_VIEWS = [
|
|
17
17
|
{ id: "limit-windows", label: "Limits" },
|
|
18
18
|
{ id: "summary", label: "Summary" },
|
|
19
19
|
{ id: "day-to-day-analyses", label: "Daily" },
|
|
20
20
|
{ id: "usage-by-model", label: "Models" }
|
|
21
21
|
];
|
|
22
|
+
const CONTROL_SECTIONS = ["provider", "view", "table"];
|
|
22
23
|
const CODEX_CREDIT_COST_USD = 0.01;
|
|
23
24
|
const LIMIT_TABLE_HEADERS = ["Scope", "Plan", "Models", "Window", "Used", "Start", "End", "API eq.", "Full"];
|
|
24
25
|
const DAILY_TABLE_HEADERS = ["Day", "Ev", "Input", "Output", "C read", "C write", "API eq."];
|
|
@@ -39,26 +40,29 @@ function App(props) {
|
|
|
39
40
|
const [providerStates, setProviderStates] = useState(providers.map((provider) => ({ provider, status: "loading" })));
|
|
40
41
|
const [selectedProviderId, setSelectedProviderId] = useState(providers[0]?.id ?? "");
|
|
41
42
|
const [hasUserSelectedProvider, setHasUserSelectedProvider] = useState(false);
|
|
42
|
-
const [
|
|
43
|
-
const [
|
|
44
|
-
const [
|
|
45
|
-
const [
|
|
43
|
+
const [selectedDetailViewIndex, setSelectedDetailViewIndex] = useState(0);
|
|
44
|
+
const [selectedControlSectionIndex, setSelectedControlSectionIndex] = useState(0);
|
|
45
|
+
const [selectedLimitRowIndex, setSelectedLimitRowIndex] = useState();
|
|
46
|
+
const [selectedDayRowIndex, setSelectedDayRowIndex] = useState();
|
|
47
|
+
const [selectedModelRowIndex, setSelectedModelRowIndex] = useState();
|
|
46
48
|
const [selectedCopilotActionIndex, setSelectedCopilotActionIndex] = useState(0);
|
|
47
49
|
const [copilotActionMessage, setCopilotActionMessage] = useState();
|
|
48
50
|
const hasReportedAnonymousUsageRef = useRef(false);
|
|
49
51
|
const sortedProviderStates = React.useMemo(() => sortProviderStatesByUsage(providerStates), [providerStates]);
|
|
50
52
|
const selectedProviderIndex = Math.max(0, sortedProviderStates.findIndex((state) => state.provider.id === selectedProviderId));
|
|
51
53
|
const selectedProvider = sortedProviderStates[selectedProviderIndex];
|
|
52
|
-
const
|
|
54
|
+
const selectedDetailView = DETAIL_VIEWS[selectedDetailViewIndex];
|
|
55
|
+
const selectedControlSection = CONTROL_SECTIONS[selectedControlSectionIndex];
|
|
56
|
+
const isTableControlSelected = selectedControlSection === "table";
|
|
53
57
|
const limitRows = getLimitRows(selectedProvider);
|
|
54
58
|
const dayRows = getDayRows(selectedProvider);
|
|
55
59
|
const modelRows = getModelRows(selectedProvider);
|
|
56
|
-
const activeLimitRowIndex =
|
|
57
|
-
const activeDayRowIndex =
|
|
58
|
-
const activeModelRowIndex =
|
|
59
|
-
const selectedLimitRow = activeLimitRowIndex >= 0 ? limitRows[activeLimitRowIndex] : undefined;
|
|
60
|
-
const selectedDayRow = activeDayRowIndex >= 0 ? dayRows[activeDayRowIndex] : undefined;
|
|
61
|
-
const selectedModelRow = activeModelRowIndex >= 0 ? modelRows[activeModelRowIndex] : undefined;
|
|
60
|
+
const activeLimitRowIndex = clampOptionalSelectionIndex(selectedLimitRowIndex, limitRows.length);
|
|
61
|
+
const activeDayRowIndex = clampOptionalSelectionIndex(selectedDayRowIndex, dayRows.length);
|
|
62
|
+
const activeModelRowIndex = clampOptionalSelectionIndex(selectedModelRowIndex, modelRows.length);
|
|
63
|
+
const selectedLimitRow = isTableControlSelected && activeLimitRowIndex >= 0 ? limitRows[activeLimitRowIndex] : undefined;
|
|
64
|
+
const selectedDayRow = isTableControlSelected && activeDayRowIndex >= 0 ? dayRows[activeDayRowIndex] : undefined;
|
|
65
|
+
const selectedModelRow = isTableControlSelected && activeModelRowIndex >= 0 ? modelRows[activeModelRowIndex] : undefined;
|
|
62
66
|
useEffect(() => {
|
|
63
67
|
let cancelled = false;
|
|
64
68
|
for (const provider of providers) {
|
|
@@ -110,6 +114,11 @@ function App(props) {
|
|
|
110
114
|
// Anonymous usage reporting is best-effort and must never disturb the TUI.
|
|
111
115
|
});
|
|
112
116
|
}, [props.usageReportingEnabled, providerStates]);
|
|
117
|
+
const clearSelectedTableRows = useCallback(() => {
|
|
118
|
+
setSelectedLimitRowIndex(undefined);
|
|
119
|
+
setSelectedDayRowIndex(undefined);
|
|
120
|
+
setSelectedModelRowIndex(undefined);
|
|
121
|
+
}, []);
|
|
113
122
|
useMouseClick((click) => {
|
|
114
123
|
const regionId = resolveClick(click);
|
|
115
124
|
if (!regionId) {
|
|
@@ -118,23 +127,36 @@ function App(props) {
|
|
|
118
127
|
if (regionId.startsWith("provider:")) {
|
|
119
128
|
setSelectedProviderId(regionId.slice("provider:".length));
|
|
120
129
|
setHasUserSelectedProvider(true);
|
|
130
|
+
setSelectedControlSectionIndex(CONTROL_SECTIONS.indexOf("provider"));
|
|
131
|
+
clearSelectedTableRows();
|
|
121
132
|
return;
|
|
122
133
|
}
|
|
123
|
-
if (regionId.startsWith("
|
|
124
|
-
|
|
134
|
+
if (regionId.startsWith("view:")) {
|
|
135
|
+
setSelectedDetailViewIndex(Number(regionId.slice("view:".length)));
|
|
136
|
+
setSelectedControlSectionIndex(CONTROL_SECTIONS.indexOf("view"));
|
|
137
|
+
clearSelectedTableRows();
|
|
125
138
|
}
|
|
126
139
|
});
|
|
127
140
|
const moveSelectedTableRow = useCallback((delta) => {
|
|
128
|
-
if (
|
|
129
|
-
|
|
141
|
+
if (selectedDetailView.id === "limit-windows") {
|
|
142
|
+
if (limitRows.length === 0) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
setSelectedLimitRowIndex(clampSelectionIndex(activeLimitRowIndex < 0 ? 0 : activeLimitRowIndex + delta, limitRows.length));
|
|
130
146
|
return;
|
|
131
147
|
}
|
|
132
|
-
if (
|
|
133
|
-
|
|
148
|
+
if (selectedDetailView.id === "usage-by-model") {
|
|
149
|
+
if (modelRows.length === 0) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
setSelectedModelRowIndex(clampSelectionIndex(activeModelRowIndex < 0 ? 0 : activeModelRowIndex + delta, modelRows.length));
|
|
134
153
|
return;
|
|
135
154
|
}
|
|
136
|
-
if (
|
|
137
|
-
|
|
155
|
+
if (selectedDetailView.id === "day-to-day-analyses") {
|
|
156
|
+
if (dayRows.length === 0) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
setSelectedDayRowIndex(clampSelectionIndex(activeDayRowIndex < 0 ? 0 : activeDayRowIndex + delta, dayRows.length));
|
|
138
160
|
}
|
|
139
161
|
}, [
|
|
140
162
|
activeDayRowIndex,
|
|
@@ -143,8 +165,59 @@ function App(props) {
|
|
|
143
165
|
dayRows.length,
|
|
144
166
|
limitRows.length,
|
|
145
167
|
modelRows.length,
|
|
146
|
-
|
|
168
|
+
selectedDetailView.id
|
|
147
169
|
]);
|
|
170
|
+
const canMoveTableRowUp = useCallback(() => {
|
|
171
|
+
if (selectedDetailView.id === "limit-windows") {
|
|
172
|
+
return activeLimitRowIndex > 0;
|
|
173
|
+
}
|
|
174
|
+
if (selectedDetailView.id === "usage-by-model") {
|
|
175
|
+
return activeModelRowIndex > 0;
|
|
176
|
+
}
|
|
177
|
+
if (selectedDetailView.id === "day-to-day-analyses") {
|
|
178
|
+
return activeDayRowIndex > 0;
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}, [activeDayRowIndex, activeLimitRowIndex, activeModelRowIndex, selectedDetailView.id]);
|
|
182
|
+
const moveSelectionDown = useCallback(() => {
|
|
183
|
+
if (selectedControlSection === "table") {
|
|
184
|
+
moveSelectedTableRow(1);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
setSelectedControlSectionIndex((current) => {
|
|
188
|
+
const next = Math.min(current + 1, CONTROL_SECTIONS.length - 1);
|
|
189
|
+
if (CONTROL_SECTIONS[next] === "table") {
|
|
190
|
+
moveSelectedTableRow(1);
|
|
191
|
+
}
|
|
192
|
+
return next;
|
|
193
|
+
});
|
|
194
|
+
}, [moveSelectedTableRow, selectedControlSection]);
|
|
195
|
+
const moveSelectionUp = useCallback(() => {
|
|
196
|
+
if (selectedControlSection === "table" && canMoveTableRowUp()) {
|
|
197
|
+
moveSelectedTableRow(-1);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (selectedControlSection === "table") {
|
|
201
|
+
clearSelectedTableRows();
|
|
202
|
+
}
|
|
203
|
+
setSelectedControlSectionIndex((current) => Math.max(current - 1, 0));
|
|
204
|
+
}, [canMoveTableRowUp, clearSelectedTableRows, moveSelectedTableRow, selectedControlSection]);
|
|
205
|
+
const changeSelectedProvider = useCallback((delta) => {
|
|
206
|
+
setSelectedProviderId(sortedProviderStates[(selectedProviderIndex + delta + sortedProviderStates.length) % sortedProviderStates.length].provider.id);
|
|
207
|
+
setHasUserSelectedProvider(true);
|
|
208
|
+
}, [selectedProviderIndex, sortedProviderStates]);
|
|
209
|
+
const changeSelectedDetailView = useCallback((delta) => {
|
|
210
|
+
setSelectedDetailViewIndex((current) => (current + delta + DETAIL_VIEWS.length) % DETAIL_VIEWS.length);
|
|
211
|
+
}, []);
|
|
212
|
+
const changeSelectedSectionValue = useCallback((delta) => {
|
|
213
|
+
if (selectedControlSection === "provider") {
|
|
214
|
+
changeSelectedProvider(delta);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (selectedControlSection === "view") {
|
|
218
|
+
changeSelectedDetailView(delta);
|
|
219
|
+
}
|
|
220
|
+
}, [changeSelectedDetailView, changeSelectedProvider, selectedControlSection]);
|
|
148
221
|
useInput((input, key) => {
|
|
149
222
|
// Mouse reports arrive as SGR escape sequences and are handled by useMouseClick.
|
|
150
223
|
// Ink strips the leading ESC, leaving e.g. "[<0;10;5M" — never treat that as a key.
|
|
@@ -160,45 +233,42 @@ function App(props) {
|
|
|
160
233
|
return;
|
|
161
234
|
}
|
|
162
235
|
if (selectedProvider.provider.id === "copilot" && key.return) {
|
|
163
|
-
runCopilotAction(
|
|
236
|
+
runCopilotAction(setCopilotActionMessage);
|
|
164
237
|
return;
|
|
165
238
|
}
|
|
166
|
-
if (
|
|
167
|
-
|
|
239
|
+
if (key.tab && !key.shift) {
|
|
240
|
+
changeSelectedProvider(1);
|
|
168
241
|
return;
|
|
169
242
|
}
|
|
170
|
-
if (
|
|
171
|
-
|
|
243
|
+
if (key.tab && key.shift) {
|
|
244
|
+
changeSelectedProvider(-1);
|
|
172
245
|
return;
|
|
173
246
|
}
|
|
174
|
-
if (
|
|
175
|
-
|
|
247
|
+
if (input === "]") {
|
|
248
|
+
changeSelectedDetailView(1);
|
|
176
249
|
return;
|
|
177
250
|
}
|
|
178
|
-
if (
|
|
179
|
-
|
|
251
|
+
if (input === "[") {
|
|
252
|
+
changeSelectedDetailView(-1);
|
|
180
253
|
return;
|
|
181
254
|
}
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
setHasUserSelectedProvider(true);
|
|
255
|
+
if (key.rightArrow) {
|
|
256
|
+
changeSelectedSectionValue(1);
|
|
185
257
|
return;
|
|
186
258
|
}
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
.provider.id);
|
|
190
|
-
setHasUserSelectedProvider(true);
|
|
259
|
+
if (key.leftArrow) {
|
|
260
|
+
changeSelectedSectionValue(-1);
|
|
191
261
|
return;
|
|
192
262
|
}
|
|
193
|
-
if (key.downArrow
|
|
194
|
-
|
|
263
|
+
if (key.downArrow) {
|
|
264
|
+
moveSelectionDown();
|
|
195
265
|
return;
|
|
196
266
|
}
|
|
197
|
-
if (key.upArrow
|
|
198
|
-
|
|
267
|
+
if (key.upArrow) {
|
|
268
|
+
moveSelectionUp();
|
|
199
269
|
}
|
|
200
270
|
});
|
|
201
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: viewportHeight, overflow: "hidden", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "cyan", children: "LetMeCode Usage Dashboard" }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, color: "white", backgroundColor: "green", children: " beta " })] }), _jsxs(Box, { children: [_jsx(
|
|
271
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: viewportHeight, overflow: "hidden", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "cyan", children: "LetMeCode Usage Dashboard" }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, color: "white", backgroundColor: "green", children: " beta " })] }), _jsxs(Box, { children: [_jsx(ControlSectionLabel, { label: "Provider", active: selectedControlSection === "provider" }), sortedProviderStates.map((state) => (_jsx(ProviderOption, { 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(ControlSectionLabel, { label: "View", active: selectedControlSection === "view" }), DETAIL_VIEWS.map((view, index) => (_jsx(ViewOption, { label: view.label, active: index === selectedDetailViewIndex, regionRef: getRegionRef(`view:${index}`) }, view.id)))] }), _jsxs(Box, { 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, viewId: selectedDetailView.id, selectedLimitRowKey: selectedLimitRow ? getLimitRowKey(selectedLimitRow) : undefined, selectedDayKey: selectedDayRow?.dayKey, selectedModelId: selectedModelRow?.modelId, availableHeight: contentPanelHeight }) }) }), _jsx(SelectionDetailsPanel, { providerState: selectedProvider, viewId: selectedDetailView.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, { 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: "\u2191/\u2193 move \u00B7 \u2190/\u2192 selected \u00B7 Tab provider \u00B7 [/] view \u00B7 q quit" })] }));
|
|
202
272
|
}
|
|
203
273
|
function CopilotActionsPanel(props) {
|
|
204
274
|
if (props.providerState.provider.id !== "copilot") {
|
|
@@ -206,9 +276,9 @@ function CopilotActionsPanel(props) {
|
|
|
206
276
|
}
|
|
207
277
|
const hasNoUsage = props.providerState.status === "ready" && props.providerState.stats.summary.tokenEvents === 0;
|
|
208
278
|
const accentColor = hasNoUsage ? "red" : "cyan";
|
|
209
|
-
return (_jsxs(Box, { borderStyle: "round", borderColor: accentColor, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: accentColor, children: "Copilot setup" }), _jsx(Box, { children: COPILOT_ACTIONS.map((action, index) => (_jsx(Box, { marginRight: 1, children: _jsx(Text, { inverse: index === (props.selectedActionIndex ?? 0), bold: hasNoUsage && action.id === "vscode", color: action.enabled ? (hasNoUsage && action.id === "vscode" ? accentColor : undefined) : "gray", children: `${index + 1} ${action.label}` }) }, action.id))) }), _jsx(Text, { color: hasNoUsage ? accentColor : "gray", children: "Press 1
|
|
279
|
+
return (_jsxs(Box, { borderStyle: "round", borderColor: accentColor, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: accentColor, children: "Copilot setup" }), _jsx(Box, { children: COPILOT_ACTIONS.map((action, index) => (_jsx(Box, { marginRight: 1, children: _jsx(Text, { inverse: index === (props.selectedActionIndex ?? 0), bold: hasNoUsage && action.id === "vscode", color: action.enabled ? (hasNoUsage && action.id === "vscode" ? accentColor : undefined) : "gray", children: `${index + 1} ${action.label}` }) }, action.id))) }), _jsx(Text, { color: hasNoUsage ? accentColor : "gray", children: "Press 1 to select an action, enter to run selected." }), props.actionMessage ? _jsx(Text, { children: props.actionMessage }) : null] }));
|
|
210
280
|
}
|
|
211
|
-
function runCopilotAction(
|
|
281
|
+
function runCopilotAction(setCopilotActionMessage) {
|
|
212
282
|
setCopilotActionMessage("Updating VS Code settings...");
|
|
213
283
|
void configureCopilotVsCodeLogging()
|
|
214
284
|
.then((result) => {
|
|
@@ -232,14 +302,17 @@ function formatCopilotLoggingResult(result) {
|
|
|
232
302
|
`"github.copilot.chat.otel.outfile": "${result.outfile}"`
|
|
233
303
|
].join("\n");
|
|
234
304
|
}
|
|
235
|
-
function
|
|
305
|
+
function ProviderOption(props) {
|
|
236
306
|
const statusColor = props.status === "error" ? "red" : props.status === "loading" ? "yellow" : "green";
|
|
237
|
-
const
|
|
238
|
-
return (_jsx(Box, { marginRight: 2, ref: props.regionRef, children: _jsx(Text, { color: statusColor, bold: props.active, children:
|
|
307
|
+
const optionLabel = props.active ? `[${props.label}]` : ` ${props.label} `;
|
|
308
|
+
return (_jsx(Box, { marginRight: 2, ref: props.regionRef, children: _jsx(Text, { color: statusColor, bold: props.active, children: optionLabel }) }));
|
|
309
|
+
}
|
|
310
|
+
function ControlSectionLabel(props) {
|
|
311
|
+
return (_jsxs(Text, { color: props.active ? "cyan" : "gray", bold: props.active, children: [props.active ? "› " : " ", pad(props.label, 10)] }));
|
|
239
312
|
}
|
|
240
|
-
function
|
|
241
|
-
const
|
|
242
|
-
return (_jsx(Box, { marginRight: 2, ref: props.regionRef, children: _jsx(Text, { wrap: "truncate-end", bold: props.active, children:
|
|
313
|
+
function ViewOption(props) {
|
|
314
|
+
const optionLabel = props.active ? `[${props.label}]` : ` ${props.label} `;
|
|
315
|
+
return (_jsx(Box, { marginRight: 2, ref: props.regionRef, children: _jsx(Text, { wrap: "truncate-end", bold: props.active, children: optionLabel }) }));
|
|
243
316
|
}
|
|
244
317
|
function SummaryPanel(props) {
|
|
245
318
|
const { summary } = props.stats;
|
|
@@ -262,13 +335,13 @@ function ContentPanel(props) {
|
|
|
262
335
|
if (props.providerState.status === "error") {
|
|
263
336
|
return _jsxs(Text, { color: "red", children: ["Provider error: ", props.providerState.errorMessage] });
|
|
264
337
|
}
|
|
265
|
-
if (props.
|
|
338
|
+
if (props.viewId === "limit-windows") {
|
|
266
339
|
return (_jsx(LimitWindowsPanel, { stats: props.providerState.stats, selectedRowKey: props.selectedLimitRowKey, availableHeight: props.availableHeight }));
|
|
267
340
|
}
|
|
268
|
-
if (props.
|
|
341
|
+
if (props.viewId === "summary") {
|
|
269
342
|
return _jsx(SummaryPanel, { stats: props.providerState.stats });
|
|
270
343
|
}
|
|
271
|
-
if (props.
|
|
344
|
+
if (props.viewId === "day-to-day-analyses") {
|
|
272
345
|
return (_jsx(DayToDayPanel, { stats: props.providerState.stats, selectedDayKey: props.selectedDayKey, availableHeight: props.availableHeight }));
|
|
273
346
|
}
|
|
274
347
|
return (_jsx(UsageByModelPanel, { stats: props.providerState.stats, selectedModelId: props.selectedModelId, availableHeight: props.availableHeight }));
|
|
@@ -463,15 +536,15 @@ function SelectionDetailsPanel(props) {
|
|
|
463
536
|
if (props.providerState.status !== "ready") {
|
|
464
537
|
return null;
|
|
465
538
|
}
|
|
466
|
-
if (props.
|
|
539
|
+
if (props.viewId === "limit-windows" && props.selectedLimitRow) {
|
|
467
540
|
const row = props.selectedLimitRow;
|
|
468
541
|
return (_jsxs(DetailsPanelFrame, { children: [_jsxs(Box, { children: [_jsxs(Box, { flexDirection: "column", width: 25, children: [_jsx(DetailRow, { label: "Plan", value: row.planType }), _jsx(DetailRow, { label: "Models", value: formatLimitWindowModels(row) }), _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) })] }), _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.cacheReadStatus, row.totals.cacheReadInputTokens) }), _jsx(DetailRow, { label: "Cache write", value: formatCacheTokens(row.totals.cacheWriteStatus, row.totals.cacheWriteInputTokens) }), _jsx(DetailRow, { label: "Output", value: formatInteger(row.totals.outputTokens) }), _jsx(DetailRow, { label: "Total", value: formatInteger(row.totals.totalTokens) })] })] }), _jsx(DetailRow, { label: "API eq.", value: formatUsageUsd(row.totals), note: "API equivalent cost" }), _jsx(DetailRow, { label: "Full value", value: formatLimitFullValueRange(row.totals, row.maxUsedPercent), note: "API equivalent cost if 100% will be used" })] }));
|
|
469
542
|
}
|
|
470
|
-
if (props.
|
|
543
|
+
if (props.viewId === "day-to-day-analyses" && props.selectedDayRow) {
|
|
471
544
|
const row = props.selectedDayRow;
|
|
472
545
|
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 })] }));
|
|
473
546
|
}
|
|
474
|
-
if (props.
|
|
547
|
+
if (props.viewId === "usage-by-model" && props.selectedModelRow) {
|
|
475
548
|
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 })] }));
|
|
476
549
|
}
|
|
477
550
|
return null;
|
|
@@ -534,21 +607,6 @@ function formatCompactNumber(value) {
|
|
|
534
607
|
minimumFractionDigits: 0
|
|
535
608
|
});
|
|
536
609
|
}
|
|
537
|
-
function formatCredits(value) {
|
|
538
|
-
if (value > 0 && value < 0.01) {
|
|
539
|
-
return "<0.01";
|
|
540
|
-
}
|
|
541
|
-
return value.toLocaleString("en-US", {
|
|
542
|
-
minimumFractionDigits: 2,
|
|
543
|
-
maximumFractionDigits: 2
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
function formatUsageCredits(totals, modelId) {
|
|
547
|
-
if (isInternalUsageModel(modelId)) {
|
|
548
|
-
return "N/A";
|
|
549
|
-
}
|
|
550
|
-
return totals.estimatedCreditsStatus === "unavailable" ? "-" : formatCredits(totals.estimatedCredits);
|
|
551
|
-
}
|
|
552
610
|
function formatUsageUsd(totals, modelId) {
|
|
553
611
|
if (isInternalUsageModel(modelId)) {
|
|
554
612
|
return "N/A";
|
|
@@ -664,13 +722,6 @@ function formatUsedPercentRange(minUsedPercent, maxUsedPercent) {
|
|
|
664
722
|
? fmt(minUsedPercent)
|
|
665
723
|
: `${fmt(minUsedPercent)}–${fmt(maxUsedPercent)}`;
|
|
666
724
|
}
|
|
667
|
-
function formatWindowMinutes(value) {
|
|
668
|
-
const hours = value / 60;
|
|
669
|
-
if (hours >= 24) {
|
|
670
|
-
return `${(hours / 24).toFixed(2)}d`;
|
|
671
|
-
}
|
|
672
|
-
return `${hours.toFixed(2)}h`;
|
|
673
|
-
}
|
|
674
725
|
function formatCompactWindowMinutes(value) {
|
|
675
726
|
const hours = value / 60;
|
|
676
727
|
if (hours >= 24) {
|
|
@@ -843,7 +894,7 @@ function parseMouseClicks(chunk) {
|
|
|
843
894
|
}
|
|
844
895
|
return clicks;
|
|
845
896
|
}
|
|
846
|
-
// Tracks the on-screen rectangle of named clickable regions
|
|
897
|
+
// Tracks the on-screen rectangle of named clickable regions via Ink
|
|
847
898
|
// refs and resolves a click coordinate back to a region id.
|
|
848
899
|
function useClickRegions() {
|
|
849
900
|
const nodesRef = useRef(new Map());
|
|
@@ -966,6 +1017,12 @@ function clampSelectionIndex(value, rowCount) {
|
|
|
966
1017
|
}
|
|
967
1018
|
return Math.max(0, Math.min(value, rowCount - 1));
|
|
968
1019
|
}
|
|
1020
|
+
function clampOptionalSelectionIndex(value, rowCount) {
|
|
1021
|
+
if (value === undefined) {
|
|
1022
|
+
return -1;
|
|
1023
|
+
}
|
|
1024
|
+
return clampSelectionIndex(value, rowCount);
|
|
1025
|
+
}
|
|
969
1026
|
function sortProviderStatesByUsage(states) {
|
|
970
1027
|
return states
|
|
971
1028
|
.map((state, index) => ({ state, index }))
|