-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathstatusBar.ts
More file actions
300 lines (263 loc) · 9.51 KB
/
Copy pathstatusBar.ts
File metadata and controls
300 lines (263 loc) · 9.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
import * as vscode from "vscode";
let statusBarItem: vscode.StatusBarItem;
/**
* Interface for reset information
*/
interface ResetInfo {
resetDate: Date;
daysRemaining: number;
resetDateStr: string;
}
/**
* Creates and displays the status bar item.
*/
export function createStatusBarItem() {
statusBarItem = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Left,
100
);
statusBarItem.command = "cursorUsage.refresh";
statusBarItem.tooltip = "Remaining Cursor fast-premium requests";
statusBarItem.text = "$(zap) Loading...";
statusBarItem.show();
}
/**
* Updates the status bar with the remaining requests, spending info, reset info, and appropriate color/icon.
* @param remainingRequests The number of requests left.
* @param totalRequests The total number of requests allowed in the cycle.
* @param usedRequests The number of requests already used in the cycle.
* @param spendCents The amount spent in cents (optional).
* @param hardLimitDollars The hard limit in dollars (optional).
* @param resetInfo Information about when the usage resets (optional).
*/
export function updateStatusBar(
remainingRequests: number,
totalRequests: number,
usedRequests: number,
spendCents?: number,
hardLimitDollars?: number,
resetInfo?: ResetInfo
) {
if (!statusBarItem) {
return;
}
const warningThreshold = totalRequests * 0.1; // 10%
let icon = "$(zap)";
// Reset background color before setting it.
statusBarItem.backgroundColor = undefined;
// Calculate spending status if data is available
let isCloseToSpendLimit = false;
let isOverSpendLimit = false;
if (spendCents !== undefined && hardLimitDollars !== undefined) {
const spendDollars = spendCents / 100;
const spendPercentage = spendDollars / hardLimitDollars;
isCloseToSpendLimit = spendPercentage >= 0.8; // 80% of spend limit
isOverSpendLimit = spendDollars >= hardLimitDollars;
}
// Determine warning/error states based on current display mode
let shouldShowError = false;
let shouldShowWarning = false;
if (remainingRequests > 0) {
// When showing requests: base colors on request status
const isLowOnRequests = remainingRequests <= warningThreshold;
shouldShowWarning = isLowOnRequests;
shouldShowError = false; // Never error state when requests remain
} else {
// When showing spending (0 requests): base colors on spending status
shouldShowError = isOverSpendLimit;
shouldShowWarning = isCloseToSpendLimit && !isOverSpendLimit;
}
// Set icon and background based on determined status
if (shouldShowError) {
icon = "$(error)";
statusBarItem.backgroundColor = new vscode.ThemeColor(
"statusBarItem.errorBackground"
);
} else if (shouldShowWarning) {
icon = "$(warning)";
statusBarItem.backgroundColor = new vscode.ThemeColor(
"statusBarItem.warningBackground"
);
}
// Build status text - primary display is remaining requests
// Only show spending when there are 0 requests left
let statusText: string;
const normalizedUsedRequests = Math.max(0, usedRequests);
const usageSummary = `${normalizedUsedRequests}/${totalRequests}`;
const spendSummary =
spendCents !== undefined && hardLimitDollars !== undefined
? `$${(spendCents / 100).toFixed(2)}/$${hardLimitDollars.toFixed(2)}`
: undefined;
if (remainingRequests > 0) {
// Show remaining requests
statusText = spendSummary
? `${icon} ${remainingRequests} · ${spendSummary}`
: `${icon} ${remainingRequests} · ${usageSummary}`;
} else {
// No requests left - show spending instead (if available)
if (spendSummary) {
statusText = `${icon} ${spendSummary}`;
} else {
// No spending data available, just show 0
statusText = `${icon} 0`;
}
}
statusBarItem.text = statusText;
// Update tooltip with detailed information
updateTooltip(
remainingRequests,
normalizedUsedRequests,
totalRequests,
spendCents,
hardLimitDollars,
resetInfo
);
}
/**
* Updates the tooltip with comprehensive usage, spending, and reset information.
* @param remainingRequests The number of requests left.
* @param usedRequests The number of requests already used in the cycle.
* @param totalRequests The total number of requests allowed in the cycle.
* @param spendCents The amount spent in cents (optional).
* @param hardLimitDollars The hard limit in dollars (optional).
* @param resetInfo Information about when the usage resets (optional).
*/
function updateTooltip(
remainingRequests: number,
usedRequests: number,
totalRequests: number,
spendCents?: number,
hardLimitDollars?: number,
resetInfo?: ResetInfo
) {
if (!statusBarItem) {
return;
}
const usageForDisplay = Math.max(0, usedRequests);
const requestPercentage = totalRequests
? ((usageForDisplay / totalRequests) * 100).toFixed(1)
: "0.0";
// Calculate cycle information once if resetInfo is available
let daysElapsed = 0;
let dailyUsageRate = 0;
if (resetInfo && resetInfo.daysRemaining > 0) {
const startOfCycle = new Date(resetInfo.resetDate);
startOfCycle.setMonth(startOfCycle.getMonth() - 1); // Go back one month to get start
const totalCycleDays = Math.ceil(
(resetInfo.resetDate.getTime() - startOfCycle.getTime()) /
(1000 * 3600 * 24)
);
daysElapsed = totalCycleDays - resetInfo.daysRemaining;
if (daysElapsed > 0) {
dailyUsageRate = parseFloat((usedRequests / daysElapsed).toFixed(1));
}
}
let tooltip = "";
// Add reset information at the top if available
if (resetInfo) {
let resetText =
resetInfo.daysRemaining === 1
? `Resets tomorrow (${resetInfo.resetDateStr})`
: resetInfo.daysRemaining === 0
? `Resets today (${resetInfo.resetDateStr})`
: `Resets in ${resetInfo.daysRemaining} days (${resetInfo.resetDateStr})`;
// Add daily usage rate to the reset line if available
if (dailyUsageRate > 0) {
resetText += ` · ${dailyUsageRate} requests/day avg`;
}
tooltip = `${resetText}\n`;
// Add warning about quota exhaustion if needed
if (remainingRequests > 0 && dailyUsageRate > 0) {
const estimatedDaysLeft = Math.ceil(remainingRequests / dailyUsageRate);
if (estimatedDaysLeft < resetInfo.daysRemaining) {
tooltip += `⚠️ At current rate, quota exhausted in ~${estimatedDaysLeft} days\n`;
}
}
tooltip += "\n";
}
// Add main request stats
tooltip += `Fast Premium Requests: ${remainingRequests}/${totalRequests} remaining (${requestPercentage}% used)`;
if (spendCents !== undefined && hardLimitDollars !== undefined) {
const spendDollars = spendCents / 100;
const spendPercentage = ((spendDollars / hardLimitDollars) * 100).toFixed(
1
);
const remainingDollars = (hardLimitDollars - spendDollars).toFixed(2);
tooltip += `\nSpending: $${spendDollars.toFixed(2)} of $${hardLimitDollars.toFixed(2)} limit (${spendPercentage}% used)`;
tooltip += `\nRemaining budget: $${remainingDollars}`;
// Add relevant warnings based on current state
if (remainingRequests > 0) {
// When showing requests, warn about low requests
if (remainingRequests <= totalRequests * 0.1) {
tooltip += `\n⚠️ Low on requests`;
}
} else {
// When showing spending, warn about spending status
if (spendDollars >= hardLimitDollars) {
tooltip += `\n⚠️ Spend limit reached`;
} else if (spendDollars / hardLimitDollars >= 0.8) {
tooltip += `\n⚠️ Approaching spend limit`;
}
}
} else {
// No spending data available
if (remainingRequests <= 0) {
tooltip += `\n⚠️ No requests remaining`;
} else if (remainingRequests <= totalRequests * 0.1) {
tooltip += `\n⚠️ Low on requests`;
}
}
tooltip += `\n\nClick to refresh`;
statusBarItem.tooltip = tooltip;
}
/**
* Sets the status bar to a generic error state.
* @param message The message to display. If not provided, a default message is used.
*/
export function setStatusBarError(message?: string) {
if (!statusBarItem) {
return;
}
const displayMessage = message || "Error";
statusBarItem.text = `$(error) ${displayMessage}`;
statusBarItem.backgroundColor = new vscode.ThemeColor(
"statusBarItem.errorBackground"
);
if (displayMessage === "Team ID?") {
statusBarItem.command = "cursorUsage.openSettings";
statusBarItem.tooltip = "Click to set your Team ID in settings";
} else {
statusBarItem.command = "cursorUsage.refresh";
statusBarItem.tooltip = "Click to refresh usage data";
}
}
/**
* Sets the status bar to a generic warning state.
* @param message The message to display.
*/
export function setStatusBarWarning(message: string) {
if (!statusBarItem) {
return;
}
statusBarItem.text = `$(warning) ${message}`;
statusBarItem.backgroundColor = new vscode.ThemeColor(
"statusBarItem.warningBackground"
);
// If the warning is about setting the cookie, make the status bar item clickable
// to trigger the cookie insertion command.
if (message === "Set Cookie") {
statusBarItem.command = "cursorUsage.insertCookie";
statusBarItem.tooltip = "Click to set your Cursor session cookie";
} else {
// Reset to default refresh command if the warning is something else
statusBarItem.command = "cursorUsage.refresh";
statusBarItem.tooltip = "Click to refresh usage data";
}
}
/**
* Returns the created status bar item instance.
* @returns The StatusBarItem instance.
*/
export function getStatusBarItem(): vscode.StatusBarItem {
return statusBarItem;
}