@jeffreycao/copilot-api 1.13.4 → 1.13.6
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/dist/main.js +1 -1
- package/dist/{server-BzgBFAIx.js → server-Cuy2sQrU.js} +38 -9
- package/dist/server-Cuy2sQrU.js.map +1 -0
- package/dist/{start-DDL0wwno.js → start-CXcuZf8x.js} +2 -2
- package/dist/{start-DDL0wwno.js.map → start-CXcuZf8x.js.map} +1 -1
- package/package.json +1 -1
- package/pages/index.html +564 -356
- package/dist/server-BzgBFAIx.js.map +0 -1
package/pages/index.html
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
|
-
<html lang="en"
|
|
2
|
+
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
@@ -14,82 +14,288 @@
|
|
|
14
14
|
rel="stylesheet"
|
|
15
15
|
/>
|
|
16
16
|
|
|
17
|
-
<script src="https://unpkg.com/lucide
|
|
17
|
+
<script src="https://unpkg.com/lucide@0.378.0/dist/umd/lucide.min.js"></script>
|
|
18
18
|
|
|
19
19
|
<style>
|
|
20
|
-
/* Gruvbox-themed color palette */
|
|
21
20
|
:root {
|
|
22
|
-
|
|
23
|
-
--color-
|
|
24
|
-
--color-
|
|
25
|
-
--color-
|
|
26
|
-
--color-
|
|
27
|
-
--color-
|
|
28
|
-
--color-
|
|
29
|
-
--color-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
--color-
|
|
34
|
-
--color-
|
|
35
|
-
--color-
|
|
36
|
-
--color-
|
|
37
|
-
--color-
|
|
38
|
-
--color-
|
|
39
|
-
|
|
40
|
-
--color-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
--color-bg-
|
|
44
|
-
--color-bg: #
|
|
45
|
-
--color-bg-light-
|
|
46
|
-
--color-bg-
|
|
47
|
-
|
|
48
|
-
--color-
|
|
49
|
-
--color-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
--color-fg-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
21
|
+
--color-red: #dc2626;
|
|
22
|
+
--color-green: #16a34a;
|
|
23
|
+
--color-yellow: #d97706;
|
|
24
|
+
--color-blue: #2563eb;
|
|
25
|
+
--color-purple: #7c3aed;
|
|
26
|
+
--color-aqua: #0891b2;
|
|
27
|
+
--color-orange: #ea580c;
|
|
28
|
+
--color-gray: #64748b;
|
|
29
|
+
|
|
30
|
+
--color-red-accent: #ef4444;
|
|
31
|
+
--color-green-accent: #22c55e;
|
|
32
|
+
--color-yellow-accent: #f59e0b;
|
|
33
|
+
--color-blue-accent: #1d4ed8;
|
|
34
|
+
--color-purple-accent: #8b5cf6;
|
|
35
|
+
--color-aqua-accent: #0ea5e9;
|
|
36
|
+
--color-orange-accent: #f97316;
|
|
37
|
+
--color-gray-accent: #94a3b8;
|
|
38
|
+
|
|
39
|
+
--color-bg-darkest: #0f172a;
|
|
40
|
+
--color-bg: #ffffff;
|
|
41
|
+
--color-bg-light-1: #f8fafc;
|
|
42
|
+
--color-bg-light-2: #e2e8f0;
|
|
43
|
+
--color-bg-light-3: #cbd5e1;
|
|
44
|
+
--color-bg-light-4: #94a3b8;
|
|
45
|
+
--color-bg-soft: #f1f5f9;
|
|
46
|
+
|
|
47
|
+
--color-fg-darker: #64748b;
|
|
48
|
+
--color-fg-dark: #475569;
|
|
49
|
+
--color-fg-medium: #334155;
|
|
50
|
+
--color-fg-light: #1e293b;
|
|
51
|
+
--color-fg-lightest: #0f172a;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
* {
|
|
55
|
+
box-sizing: border-box;
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
/* Custom styles using the new palette */
|
|
60
58
|
body {
|
|
61
59
|
font-family: "Inter", sans-serif;
|
|
62
|
-
background-color:
|
|
60
|
+
background-color: #eef2f7;
|
|
63
61
|
color: var(--color-fg-light);
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
.app-shell {
|
|
65
|
+
min-height: 100vh;
|
|
66
|
+
background: linear-gradient(180deg, #f8fafc 0%, #eef2f7 100%);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.main-shell {
|
|
70
|
+
min-width: 0;
|
|
71
|
+
padding: 28px 32px 40px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.topbar {
|
|
75
|
+
margin: 0 auto 18px;
|
|
76
|
+
max-width: 1720px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.topbar-row {
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: flex-start;
|
|
82
|
+
justify-content: space-between;
|
|
83
|
+
gap: 20px;
|
|
84
|
+
margin-bottom: 16px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.page-title {
|
|
88
|
+
font-size: clamp(28px, 3vw, 38px);
|
|
89
|
+
font-weight: 750;
|
|
90
|
+
line-height: 1.05;
|
|
91
|
+
color: var(--color-fg-lightest);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.page-subtitle {
|
|
95
|
+
margin-top: 8px;
|
|
96
|
+
color: var(--color-gray);
|
|
97
|
+
font-size: 14px;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.control-surface {
|
|
101
|
+
display: grid;
|
|
102
|
+
grid-template-columns:
|
|
103
|
+
minmax(320px, 1fr)
|
|
104
|
+
minmax(220px, 320px)
|
|
105
|
+
150px
|
|
106
|
+
132px;
|
|
107
|
+
gap: 12px;
|
|
108
|
+
align-items: end;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.field-label {
|
|
112
|
+
display: block;
|
|
113
|
+
margin-bottom: 6px;
|
|
114
|
+
color: var(--color-fg-medium);
|
|
115
|
+
font-size: 12px;
|
|
116
|
+
font-weight: 700;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.input-control,
|
|
120
|
+
.select-control {
|
|
121
|
+
width: 100%;
|
|
122
|
+
height: 42px;
|
|
123
|
+
border: 1px solid var(--color-bg-light-3);
|
|
124
|
+
border-radius: 8px;
|
|
125
|
+
background: rgba(255, 255, 255, 0.88);
|
|
126
|
+
color: var(--color-fg-lightest);
|
|
127
|
+
font-size: 14px;
|
|
128
|
+
outline: none;
|
|
129
|
+
transition:
|
|
130
|
+
border-color 0.16s ease,
|
|
131
|
+
box-shadow 0.16s ease,
|
|
132
|
+
background-color 0.16s ease;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.input-control {
|
|
136
|
+
padding: 0 12px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.select-control {
|
|
140
|
+
padding: 0 34px 0 12px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.input-control::placeholder {
|
|
144
|
+
color: #94a3b8;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.primary-action {
|
|
148
|
+
height: 42px;
|
|
149
|
+
display: inline-flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
justify-content: center;
|
|
152
|
+
gap: 8px;
|
|
153
|
+
border: 1px solid transparent;
|
|
154
|
+
border-radius: 8px;
|
|
155
|
+
background: var(--color-blue);
|
|
156
|
+
color: #ffffff;
|
|
157
|
+
font-size: 14px;
|
|
158
|
+
font-weight: 700;
|
|
159
|
+
transition:
|
|
160
|
+
background-color 0.16s ease,
|
|
161
|
+
transform 0.16s ease;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.primary-action:hover {
|
|
165
|
+
background: var(--color-blue-accent);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.content-area {
|
|
169
|
+
max-width: 1720px;
|
|
170
|
+
margin: 0 auto;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.dashboard-section {
|
|
174
|
+
margin-bottom: 16px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.section-heading {
|
|
178
|
+
display: flex;
|
|
179
|
+
align-items: center;
|
|
180
|
+
gap: 9px;
|
|
181
|
+
margin-bottom: 12px;
|
|
182
|
+
color: var(--color-fg-lightest);
|
|
183
|
+
font-size: 16px;
|
|
184
|
+
font-weight: 750;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.section-subtitle {
|
|
188
|
+
margin-top: -6px;
|
|
189
|
+
margin-bottom: 12px;
|
|
190
|
+
color: var(--color-gray);
|
|
191
|
+
font-size: 12px;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.panel {
|
|
195
|
+
border: 1px solid var(--color-bg-light-2);
|
|
196
|
+
border-radius: 8px;
|
|
197
|
+
background: rgba(255, 255, 255, 0.94);
|
|
198
|
+
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.05);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.panel-header {
|
|
202
|
+
display: flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
justify-content: space-between;
|
|
205
|
+
gap: 12px;
|
|
206
|
+
min-height: 54px;
|
|
207
|
+
padding: 14px 16px;
|
|
208
|
+
border-bottom: 1px solid var(--color-bg-light-2);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.panel-title {
|
|
212
|
+
color: var(--color-fg-lightest);
|
|
213
|
+
font-size: 14px;
|
|
214
|
+
font-weight: 750;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.panel-meta {
|
|
218
|
+
margin-top: 3px;
|
|
219
|
+
color: var(--color-gray);
|
|
220
|
+
font-size: 12px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.metric-grid {
|
|
224
|
+
display: grid;
|
|
225
|
+
grid-template-columns: repeat(auto-fit, minmax(185px, 1fr));
|
|
226
|
+
gap: 14px;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.metric-card {
|
|
230
|
+
min-height: 112px;
|
|
231
|
+
padding: 16px;
|
|
232
|
+
border: 1px solid var(--color-bg-light-2);
|
|
233
|
+
border-radius: 8px;
|
|
234
|
+
background: rgba(255, 255, 255, 0.96);
|
|
235
|
+
box-shadow: 0 10px 22px rgba(15, 23, 42, 0.04);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.metric-label {
|
|
239
|
+
display: flex;
|
|
240
|
+
align-items: center;
|
|
241
|
+
gap: 8px;
|
|
242
|
+
color: var(--color-fg-medium);
|
|
243
|
+
font-size: 13px;
|
|
244
|
+
font-weight: 650;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.metric-dot {
|
|
248
|
+
width: 10px;
|
|
249
|
+
height: 10px;
|
|
250
|
+
border-radius: 999px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.metric-value {
|
|
254
|
+
margin-top: 16px;
|
|
255
|
+
color: var(--color-fg-lightest);
|
|
256
|
+
font-size: clamp(20px, 2vw, 28px);
|
|
257
|
+
font-weight: 800;
|
|
258
|
+
line-height: 1.1;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.metric-note {
|
|
262
|
+
margin-top: 8px;
|
|
263
|
+
color: var(--color-gray);
|
|
264
|
+
font-size: 12px;
|
|
265
|
+
}
|
|
266
|
+
|
|
67
267
|
.progress-bar-bg {
|
|
68
|
-
background-color: var(--color-bg-
|
|
268
|
+
background-color: var(--color-bg-soft);
|
|
269
|
+
border-radius: 999px;
|
|
270
|
+
overflow: hidden;
|
|
69
271
|
}
|
|
272
|
+
|
|
70
273
|
.progress-bar-fg {
|
|
274
|
+
border-radius: 999px;
|
|
71
275
|
transition: width 0.5s ease-in-out;
|
|
72
276
|
}
|
|
73
277
|
|
|
74
|
-
/* Custom scrollbar for the raw data view */
|
|
75
278
|
.code-block::-webkit-scrollbar {
|
|
76
279
|
width: 8px;
|
|
77
280
|
height: 8px;
|
|
78
281
|
}
|
|
282
|
+
|
|
79
283
|
.code-block::-webkit-scrollbar-track {
|
|
80
|
-
background: var(--color-bg);
|
|
284
|
+
background: var(--color-bg-light-1);
|
|
81
285
|
}
|
|
286
|
+
|
|
82
287
|
.code-block::-webkit-scrollbar-thumb {
|
|
83
288
|
background: var(--color-bg-light-3);
|
|
289
|
+
border-radius: 999px;
|
|
84
290
|
}
|
|
291
|
+
|
|
85
292
|
.code-block::-webkit-scrollbar-thumb:hover {
|
|
86
293
|
background: var(--color-bg-light-4);
|
|
87
294
|
}
|
|
88
295
|
|
|
89
|
-
/* Style for focus rings to use variables */
|
|
90
296
|
.input-focus:focus {
|
|
91
|
-
--tw-ring-color: var(--color-blue);
|
|
92
297
|
border-color: var(--color-blue);
|
|
298
|
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.14);
|
|
93
299
|
}
|
|
94
300
|
|
|
95
301
|
button:disabled,
|
|
@@ -98,155 +304,124 @@
|
|
|
98
304
|
opacity: 0.55;
|
|
99
305
|
}
|
|
100
306
|
|
|
307
|
+
.data-table {
|
|
308
|
+
width: 100%;
|
|
309
|
+
border-collapse: separate;
|
|
310
|
+
border-spacing: 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.data-table thead {
|
|
314
|
+
background: var(--color-bg-light-1);
|
|
315
|
+
color: var(--color-fg-medium);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.data-table th {
|
|
319
|
+
font-size: 12px;
|
|
320
|
+
font-weight: 750;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.data-table td {
|
|
324
|
+
font-size: 12px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.data-table th,
|
|
328
|
+
.data-table td {
|
|
329
|
+
border-bottom: 1px solid var(--color-bg-light-2);
|
|
330
|
+
}
|
|
101
331
|
|
|
332
|
+
.pill-button {
|
|
333
|
+
border: 1px solid var(--color-bg-light-2);
|
|
334
|
+
border-radius: 7px;
|
|
335
|
+
background: var(--color-bg);
|
|
336
|
+
color: var(--color-fg-medium);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.quick-state-grid {
|
|
340
|
+
display: grid;
|
|
341
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
342
|
+
gap: 12px;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
@media (max-width: 1100px) {
|
|
346
|
+
.control-surface {
|
|
347
|
+
grid-template-columns: minmax(0, 1fr) 170px 132px;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.endpoint-field {
|
|
351
|
+
grid-column: 1 / -1;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
@media (max-width: 640px) {
|
|
356
|
+
.main-shell {
|
|
357
|
+
padding: 20px 14px 28px;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.topbar-row {
|
|
361
|
+
display: block;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.control-surface {
|
|
365
|
+
grid-template-columns: 1fr;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.endpoint-field {
|
|
369
|
+
grid-column: auto;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
102
372
|
</style>
|
|
103
373
|
</head>
|
|
104
374
|
<body class="antialiased">
|
|
105
|
-
<div id="app" class="
|
|
106
|
-
<div class="
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
115
|
-
width="24"
|
|
116
|
-
height="24"
|
|
117
|
-
viewBox="0 0 24 24"
|
|
118
|
-
fill="none"
|
|
119
|
-
stroke="currentColor"
|
|
120
|
-
stroke-width="2"
|
|
121
|
-
stroke-linecap="round"
|
|
122
|
-
stroke-linejoin="round"
|
|
123
|
-
class="lucide lucide-gauge-circle h-7 w-7"
|
|
124
|
-
style="color: var(--color-aqua-accent)"
|
|
125
|
-
>
|
|
126
|
-
<path d="M15.6 3.3a10 10 0 1 0 5.1 5.1" />
|
|
127
|
-
<path
|
|
128
|
-
d="M12 12a1 1 0 0 0-1-1v4a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1h-3z"
|
|
129
|
-
/>
|
|
130
|
-
<path d="M12 6.8a10 10 0 0 0 -3.2 7.2" />
|
|
131
|
-
</svg>
|
|
132
|
-
<span>Copilot API Usage Dashboard</span>
|
|
133
|
-
</h1>
|
|
134
|
-
<p class="mt-1 text-sm" style="color: var(--color-gray)">
|
|
135
|
-
Should be the same as the one in VSCode
|
|
136
|
-
</p>
|
|
137
|
-
</header>
|
|
375
|
+
<div id="app" class="app-shell">
|
|
376
|
+
<div class="main-shell">
|
|
377
|
+
<header class="topbar">
|
|
378
|
+
<div class="topbar-row">
|
|
379
|
+
<div>
|
|
380
|
+
<h1 class="page-title">Token Usage</h1>
|
|
381
|
+
<p class="page-subtitle">Copilot quota and token billing dashboard</p>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
138
384
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
style="
|
|
143
|
-
background-color: var(--color-bg-soft);
|
|
144
|
-
border-color: var(--color-bg-light-2);
|
|
145
|
-
"
|
|
146
|
-
>
|
|
147
|
-
<form
|
|
148
|
-
id="endpoint-form"
|
|
149
|
-
class="grid grid-cols-1 xl:grid-cols-[minmax(0,2fr)_minmax(0,1fr)_180px_auto] gap-3 items-end"
|
|
150
|
-
>
|
|
151
|
-
<div class="w-full">
|
|
152
|
-
<label
|
|
153
|
-
for="endpoint-url"
|
|
154
|
-
class="mb-1 block font-semibold whitespace-nowrap text-sm"
|
|
155
|
-
style="color: var(--color-fg-lightest)"
|
|
156
|
-
>Usage Endpoint URL</label
|
|
157
|
-
>
|
|
385
|
+
<form id="endpoint-form" class="control-surface">
|
|
386
|
+
<div class="endpoint-field">
|
|
387
|
+
<label for="endpoint-url" class="field-label">Usage Endpoint URL</label>
|
|
158
388
|
<input
|
|
159
389
|
type="text"
|
|
160
390
|
id="endpoint-url"
|
|
161
|
-
class="
|
|
162
|
-
style="
|
|
163
|
-
background-color: var(--color-bg-darkest);
|
|
164
|
-
border-color: var(--color-bg-light-3);
|
|
165
|
-
color: var(--color-fg-medium);
|
|
166
|
-
"
|
|
391
|
+
class="input-control input-focus"
|
|
167
392
|
placeholder="http://localhost:4141/usage"
|
|
168
393
|
/>
|
|
169
394
|
</div>
|
|
170
|
-
<div
|
|
171
|
-
<label
|
|
172
|
-
for="x-api-key"
|
|
173
|
-
class="mb-1 block font-semibold whitespace-nowrap text-sm"
|
|
174
|
-
style="color: var(--color-fg-lightest)"
|
|
175
|
-
>x-api-key</label
|
|
176
|
-
>
|
|
395
|
+
<div>
|
|
396
|
+
<label for="x-api-key" class="field-label">x-api-key</label>
|
|
177
397
|
<input
|
|
178
398
|
type="password"
|
|
179
399
|
id="x-api-key"
|
|
180
|
-
class="
|
|
181
|
-
style="
|
|
182
|
-
background-color: var(--color-bg-darkest);
|
|
183
|
-
border-color: var(--color-bg-light-3);
|
|
184
|
-
color: var(--color-fg-medium);
|
|
185
|
-
"
|
|
400
|
+
class="input-control input-focus"
|
|
186
401
|
placeholder="Optional when auth is enabled"
|
|
187
402
|
autocomplete="off"
|
|
188
403
|
spellcheck="false"
|
|
189
404
|
/>
|
|
190
405
|
</div>
|
|
191
|
-
<div
|
|
192
|
-
<label
|
|
193
|
-
for="token-usage-period"
|
|
194
|
-
class="mb-1 block font-semibold whitespace-nowrap text-sm"
|
|
195
|
-
style="color: var(--color-fg-lightest)"
|
|
196
|
-
>Token Usage Period</label
|
|
197
|
-
>
|
|
406
|
+
<div>
|
|
407
|
+
<label for="token-usage-period" class="field-label">Period</label>
|
|
198
408
|
<select
|
|
199
409
|
id="token-usage-period"
|
|
200
|
-
class="
|
|
201
|
-
style="
|
|
202
|
-
background-color: var(--color-bg-darkest);
|
|
203
|
-
border-color: var(--color-bg-light-3);
|
|
204
|
-
color: var(--color-fg-medium);
|
|
205
|
-
"
|
|
410
|
+
class="select-control input-focus"
|
|
206
411
|
>
|
|
207
412
|
<option value="day">Day</option>
|
|
208
|
-
<option value="week">
|
|
209
|
-
<option value="month">
|
|
413
|
+
<option value="week">Week</option>
|
|
414
|
+
<option value="month">Month</option>
|
|
210
415
|
</select>
|
|
211
416
|
</div>
|
|
212
|
-
<button
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
class="w-full xl:w-auto font-bold py-2 px-5 transition-colors flex items-center justify-center gap-2 text-sm"
|
|
216
|
-
style="
|
|
217
|
-
background-color: var(--color-blue);
|
|
218
|
-
color: var(--color-bg-darkest);
|
|
219
|
-
"
|
|
220
|
-
>
|
|
221
|
-
<svg
|
|
222
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
223
|
-
width="24"
|
|
224
|
-
height="24"
|
|
225
|
-
viewBox="0 0 24 24"
|
|
226
|
-
fill="none"
|
|
227
|
-
stroke="currentColor"
|
|
228
|
-
stroke-width="2"
|
|
229
|
-
stroke-linecap="round"
|
|
230
|
-
stroke-linejoin="round"
|
|
231
|
-
class="lucide lucide-refresh-cw h-4 w-4"
|
|
232
|
-
>
|
|
233
|
-
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />
|
|
234
|
-
<path d="M21 3v5h-5" />
|
|
235
|
-
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />
|
|
236
|
-
<path d="M3 21v-5h5" />
|
|
237
|
-
</svg>
|
|
238
|
-
<span>Fetch</span>
|
|
417
|
+
<button id="fetch-button" type="submit" class="primary-action">
|
|
418
|
+
<i data-lucide="refresh-cw" class="h-4 w-4"></i>
|
|
419
|
+
<span>Refresh</span>
|
|
239
420
|
</button>
|
|
240
421
|
</form>
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
<code>x-api-key</code> to all requests when provided.
|
|
245
|
-
</p>
|
|
246
|
-
</div>
|
|
247
|
-
|
|
248
|
-
<!-- Content Area for dynamic data -->
|
|
249
|
-
<main id="content-area"></main>
|
|
422
|
+
</header>
|
|
423
|
+
|
|
424
|
+
<main id="content-area" class="content-area"></main>
|
|
250
425
|
</div>
|
|
251
426
|
</div>
|
|
252
427
|
|
|
@@ -492,7 +667,9 @@
|
|
|
492
667
|
|
|
493
668
|
function updateControls() {
|
|
494
669
|
fetchButton.disabled = state.isLoading;
|
|
495
|
-
fetchButtonLabel.textContent = state.isLoading
|
|
670
|
+
fetchButtonLabel.textContent = state.isLoading
|
|
671
|
+
? "Refreshing..."
|
|
672
|
+
: "Refresh";
|
|
496
673
|
tokenUsagePeriodSelect.disabled =
|
|
497
674
|
state.isLoading || state.isTokenUsageLoading;
|
|
498
675
|
}
|
|
@@ -601,13 +778,13 @@
|
|
|
601
778
|
contentArea.innerHTML = renderWelcomeMessage();
|
|
602
779
|
} else if (state.error && !state.data) {
|
|
603
780
|
contentArea.innerHTML = `
|
|
604
|
-
${renderError(state.error, "Usage request failed")}
|
|
605
781
|
${renderTokenUsageSection()}
|
|
782
|
+
${renderUsageErrorSection()}
|
|
606
783
|
`;
|
|
607
784
|
} else if (state.data) {
|
|
608
785
|
contentArea.innerHTML = `
|
|
609
|
-
${renderUsageQuotas(state.data.quota_snapshots)}
|
|
610
786
|
${renderTokenUsageSection()}
|
|
787
|
+
${renderUsageQuotas(state.data.quota_snapshots)}
|
|
611
788
|
${renderDetailedData(state.data)}
|
|
612
789
|
`;
|
|
613
790
|
} else {
|
|
@@ -633,15 +810,32 @@
|
|
|
633
810
|
.join("");
|
|
634
811
|
|
|
635
812
|
return `
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
813
|
+
<section id="usage-quotas" class="dashboard-section">
|
|
814
|
+
<h2 class="section-heading">
|
|
815
|
+
<i data-lucide="gauge" class="h-5 w-5" style="color: var(--color-blue);"></i>
|
|
816
|
+
<span>Copilot Quotas</span>
|
|
817
|
+
</h2>
|
|
818
|
+
<div class="metric-grid">
|
|
819
|
+
${quotaCards}
|
|
820
|
+
</div>
|
|
821
|
+
</section>
|
|
822
|
+
`;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function renderUsageErrorSection() {
|
|
826
|
+
if (!state.error || state.data) {
|
|
827
|
+
return "";
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
return `
|
|
831
|
+
<section id="usage-quotas" class="dashboard-section">
|
|
832
|
+
<h2 class="section-heading">
|
|
833
|
+
<i data-lucide="gauge" class="h-5 w-5" style="color: var(--color-blue);"></i>
|
|
834
|
+
<span>Copilot Quotas</span>
|
|
835
|
+
</h2>
|
|
836
|
+
${renderError(state.error, "Usage request failed")}
|
|
837
|
+
</section>
|
|
838
|
+
`;
|
|
645
839
|
}
|
|
646
840
|
|
|
647
841
|
/**
|
|
@@ -665,26 +859,29 @@
|
|
|
665
859
|
if (unlimited) progressBarColor = "var(--color-blue)";
|
|
666
860
|
|
|
667
861
|
return `
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
862
|
+
<div class="metric-card">
|
|
863
|
+
<div class="flex items-center justify-between gap-3">
|
|
864
|
+
<div class="metric-label capitalize">
|
|
865
|
+
<span class="metric-dot" style="background-color: ${progressBarColor};"></span>
|
|
866
|
+
<span>${escapeHtml(title.replace(/_/g, " "))}</span>
|
|
867
|
+
</div>
|
|
868
|
+
${
|
|
869
|
+
unlimited
|
|
870
|
+
? `<span class="rounded-md px-2 py-1 text-xs font-semibold" style="background-color: rgba(37, 99, 235, 0.1); color: var(--color-blue);">Unlimited</span>`
|
|
871
|
+
: `<span class="text-xs font-semibold" style="color: var(--color-gray);">${percentUsed.toFixed(1)}% used</span>`
|
|
872
|
+
}
|
|
873
|
+
</div>
|
|
874
|
+
<div class="mt-5">
|
|
875
|
+
<div class="w-full progress-bar-bg h-2">
|
|
876
|
+
<div class="progress-bar-fg h-2" style="width: ${unlimited ? 100 : percentUsed}%; background-color: ${progressBarColor};"></div>
|
|
877
|
+
</div>
|
|
878
|
+
</div>
|
|
879
|
+
<div class="mt-4 flex items-center justify-between gap-4 text-xs font-medium" style="color: var(--color-gray);">
|
|
880
|
+
<span>${used} / ${unlimited ? "∞" : entitlement.toLocaleString()}</span>
|
|
881
|
+
<span>${unlimited ? "∞" : remaining.toLocaleString()} remaining</span>
|
|
882
|
+
</div>
|
|
883
|
+
</div>
|
|
884
|
+
`;
|
|
688
885
|
}
|
|
689
886
|
|
|
690
887
|
/**
|
|
@@ -694,7 +891,7 @@
|
|
|
694
891
|
*/
|
|
695
892
|
function formatObject(obj) {
|
|
696
893
|
if (obj === null || typeof obj !== "object") {
|
|
697
|
-
return `<span style="color: var(--color-green
|
|
894
|
+
return `<span style="color: var(--color-green);">${escapeHtml(JSON.stringify(obj))}</span>`;
|
|
698
895
|
}
|
|
699
896
|
|
|
700
897
|
return (
|
|
@@ -707,18 +904,18 @@
|
|
|
707
904
|
if (Array.isArray(value)) {
|
|
708
905
|
displayValue =
|
|
709
906
|
value.length > 0
|
|
710
|
-
? `<span style='color: var(--color-gray
|
|
711
|
-
: `<span style='color: var(--color-gray
|
|
907
|
+
? `<span style='color: var(--color-gray)'>[...${value.length} items]</span>`
|
|
908
|
+
: `<span style='color: var(--color-gray)'>[]</span>`;
|
|
712
909
|
} else if (typeof value === "object" && value !== null) {
|
|
713
910
|
displayValue = formatObject(value);
|
|
714
911
|
} else if (typeof value === "boolean") {
|
|
715
|
-
displayValue = `<span class="font-semibold" style="color: ${value ? "var(--color-green
|
|
912
|
+
displayValue = `<span class="font-semibold" style="color: ${value ? "var(--color-green)" : "var(--color-red)"}">${escapeHtml(String(value))}</span>`;
|
|
716
913
|
} else {
|
|
717
|
-
displayValue = `<span style="color: var(--color-blue
|
|
914
|
+
displayValue = `<span style="color: var(--color-blue);">${escapeHtml(JSON.stringify(value))}</span>`;
|
|
718
915
|
}
|
|
719
916
|
|
|
720
917
|
return `<div class="mt-1">
|
|
721
|
-
<span class="capitalize font-semibold" style="color: var(--color-fg-
|
|
918
|
+
<span class="capitalize font-semibold" style="color: var(--color-fg-lightest);">${formattedKey}:</span>
|
|
722
919
|
${typeof value === "object" && value !== null && !Array.isArray(value) ? displayValue : ` ${displayValue}`}
|
|
723
920
|
</div>`;
|
|
724
921
|
})
|
|
@@ -739,15 +936,16 @@
|
|
|
739
936
|
|
|
740
937
|
const formattedDetails = formatObject(data);
|
|
741
938
|
return `
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
939
|
+
<section id="detailed-data" class="dashboard-section">
|
|
940
|
+
<h2 class="section-heading">
|
|
941
|
+
<i data-lucide="file-text" class="h-5 w-5" style="color: var(--color-blue);"></i>
|
|
942
|
+
<span>Copilot Usage API Response</span>
|
|
943
|
+
</h2>
|
|
944
|
+
<div class="panel p-4 relative font-mono text-xs code-block overflow-auto" style="max-height: 420px;">
|
|
945
|
+
${formattedDetails}
|
|
946
|
+
</div>
|
|
947
|
+
</section>
|
|
948
|
+
`;
|
|
751
949
|
}
|
|
752
950
|
|
|
753
951
|
function renderTokenUsageSection() {
|
|
@@ -774,31 +972,32 @@
|
|
|
774
972
|
summary?.range || dailySummary?.range || eventsPage?.range || null;
|
|
775
973
|
const totalPages = eventsPage ? Math.max(eventsPage.total_pages, 1) : 1;
|
|
776
974
|
const eventsMeta = eventsPage
|
|
777
|
-
? `Page ${eventsPage.page} / ${totalPages}
|
|
975
|
+
? `Page ${eventsPage.page} / ${totalPages} - ${formatNumber(
|
|
778
976
|
eventsPage.total
|
|
779
977
|
)} events`
|
|
780
978
|
: "No detail page loaded yet.";
|
|
781
979
|
|
|
782
980
|
return `
|
|
783
|
-
<section id="token-usage" class="
|
|
784
|
-
<div class="mb-3 flex flex-col gap-2 sm:flex-row sm:items-
|
|
981
|
+
<section id="token-usage" class="dashboard-section">
|
|
982
|
+
<div class="mb-3 flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
|
|
785
983
|
<div>
|
|
786
|
-
<h2 class="
|
|
787
|
-
<i data-lucide="database"></i>
|
|
984
|
+
<h2 class="section-heading mb-1">
|
|
985
|
+
<i data-lucide="database" class="h-5 w-5" style="color: var(--color-blue);"></i>
|
|
986
|
+
<span>Token Usage</span>
|
|
788
987
|
</h2>
|
|
789
|
-
<p class="
|
|
988
|
+
<p class="section-subtitle mb-0">${escapeHtml(
|
|
790
989
|
renderTokenUsageRangeText(state.tokenUsagePeriod, activeRange)
|
|
791
990
|
)}</p>
|
|
792
991
|
</div>
|
|
793
|
-
<div class="flex flex-wrap items-center gap-2 text-xs" style="color: var(--color-gray
|
|
992
|
+
<div class="flex flex-wrap items-center gap-2 text-xs font-medium" style="color: var(--color-gray);">
|
|
794
993
|
${
|
|
795
994
|
state.isTokenUsageLoading
|
|
796
|
-
? "
|
|
995
|
+
? `<span class="rounded-md px-2 py-1" style="background-color: rgba(37, 99, 235, 0.08); color: var(--color-blue);">Refreshing summary...</span>`
|
|
797
996
|
: ""
|
|
798
997
|
}
|
|
799
998
|
${
|
|
800
999
|
state.isEventsLoading
|
|
801
|
-
? "
|
|
1000
|
+
? `<span class="rounded-md px-2 py-1" style="background-color: rgba(37, 99, 235, 0.08); color: var(--color-blue);">Refreshing details...</span>`
|
|
802
1001
|
: ""
|
|
803
1002
|
}
|
|
804
1003
|
</div>
|
|
@@ -813,7 +1012,7 @@
|
|
|
813
1012
|
: ""
|
|
814
1013
|
}
|
|
815
1014
|
|
|
816
|
-
<div class="
|
|
1015
|
+
<div class="metric-grid ${
|
|
817
1016
|
state.isTokenUsageLoading ? "opacity-60" : ""
|
|
818
1017
|
}">
|
|
819
1018
|
${renderTokenUsageMetric(
|
|
@@ -858,48 +1057,48 @@
|
|
|
858
1057
|
state.tokenUsageDailyError
|
|
859
1058
|
)}
|
|
860
1059
|
|
|
861
|
-
<div class="mt-4
|
|
862
|
-
<div class="
|
|
863
|
-
<div>
|
|
864
|
-
<
|
|
865
|
-
|
|
1060
|
+
<div class="mt-4 grid grid-cols-1 2xl:grid-cols-[minmax(0,1fr)_minmax(0,1.18fr)] gap-4">
|
|
1061
|
+
<div class="panel">
|
|
1062
|
+
<div class="panel-header">
|
|
1063
|
+
<div>
|
|
1064
|
+
<p class="panel-title">Model Breakdown</p>
|
|
1065
|
+
<p class="panel-meta">${summary ? `${formatNumber(summary.byModel.length)} models` : "No summary rows loaded."}</p>
|
|
1066
|
+
</div>
|
|
866
1067
|
</div>
|
|
1068
|
+
${renderTokenUsageModelBreakdown(summary)}
|
|
867
1069
|
</div>
|
|
868
|
-
${renderTokenUsageModelBreakdown(summary)}
|
|
869
|
-
</div>
|
|
870
1070
|
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
)}
|
|
1071
|
+
<div class="panel">
|
|
1072
|
+
<div class="panel-header flex-col items-start sm:flex-row sm:items-center">
|
|
1073
|
+
<div>
|
|
1074
|
+
<p class="panel-title">Request Events</p>
|
|
1075
|
+
<p class="panel-meta">${escapeHtml(eventsMeta)}</p>
|
|
1076
|
+
</div>
|
|
1077
|
+
<div class="flex items-center gap-2">
|
|
1078
|
+
${renderPaginationButton(
|
|
1079
|
+
"previous",
|
|
1080
|
+
"Previous",
|
|
1081
|
+
!eventsPage || state.isEventsLoading || eventsPage.page <= 1
|
|
1082
|
+
)}
|
|
1083
|
+
${renderPaginationButton(
|
|
1084
|
+
"next",
|
|
1085
|
+
"Next",
|
|
1086
|
+
!eventsPage
|
|
1087
|
+
|| state.isEventsLoading
|
|
1088
|
+
|| eventsPage.page >= totalPages
|
|
1089
|
+
)}
|
|
1090
|
+
</div>
|
|
892
1091
|
</div>
|
|
1092
|
+
${
|
|
1093
|
+
state.tokenUsageEventsError
|
|
1094
|
+
? `<div class="p-4">${renderError(
|
|
1095
|
+
state.tokenUsageEventsError,
|
|
1096
|
+
"Token usage details failed"
|
|
1097
|
+
)}</div>`
|
|
1098
|
+
: ""
|
|
1099
|
+
}
|
|
1100
|
+
${renderTokenUsageEventsTable(eventsPage)}
|
|
893
1101
|
</div>
|
|
894
|
-
${
|
|
895
|
-
state.tokenUsageEventsError
|
|
896
|
-
? `<div class="p-4">${renderError(
|
|
897
|
-
state.tokenUsageEventsError,
|
|
898
|
-
"Token usage details failed"
|
|
899
|
-
)}</div>`
|
|
900
|
-
: ""
|
|
901
|
-
}
|
|
902
|
-
${renderTokenUsageEventsTable(eventsPage)}
|
|
903
1102
|
</div>
|
|
904
1103
|
</section>
|
|
905
1104
|
`;
|
|
@@ -973,28 +1172,84 @@
|
|
|
973
1172
|
].join("");
|
|
974
1173
|
|
|
975
1174
|
return `
|
|
976
|
-
<div class="mt-4
|
|
977
|
-
<div class="
|
|
978
|
-
<
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1175
|
+
<div class="mt-4 grid grid-cols-1 xl:grid-cols-[minmax(0,1fr)_304px] gap-4">
|
|
1176
|
+
<div class="panel">
|
|
1177
|
+
<div class="panel-header">
|
|
1178
|
+
<div>
|
|
1179
|
+
<p class="panel-title">Token Usage Over Time</p>
|
|
1180
|
+
<p class="panel-meta">${escapeHtml(
|
|
1181
|
+
renderTokenUsageRangeText(
|
|
1182
|
+
state.tokenUsagePeriod,
|
|
1183
|
+
dailySummary?.range || null
|
|
1184
|
+
)
|
|
1185
|
+
)}</p>
|
|
1186
|
+
</div>
|
|
1187
|
+
</div>
|
|
1188
|
+
${
|
|
1189
|
+
error
|
|
1190
|
+
? `<div class="p-4">${renderError(
|
|
1191
|
+
error,
|
|
1192
|
+
"Token usage daily trend failed"
|
|
1193
|
+
)}</div>`
|
|
1194
|
+
: ""
|
|
1195
|
+
}
|
|
1196
|
+
${renderTokenUsageTrendChart(dailySummary, selectedModel)}
|
|
984
1197
|
</div>
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1198
|
+
|
|
1199
|
+
<aside class="panel">
|
|
1200
|
+
<div class="panel-header">
|
|
1201
|
+
<div>
|
|
1202
|
+
<p class="panel-title">Filters</p>
|
|
1203
|
+
<p class="panel-meta">Applied to the trend chart</p>
|
|
1204
|
+
</div>
|
|
1205
|
+
</div>
|
|
1206
|
+
<div class="p-4 space-y-4">
|
|
1207
|
+
<div>
|
|
1208
|
+
<label class="field-label">Model</label>
|
|
1209
|
+
<select
|
|
1210
|
+
data-token-usage-trend-model
|
|
1211
|
+
class="select-control input-focus text-sm"
|
|
1212
|
+
>${options}</select>
|
|
1213
|
+
</div>
|
|
1214
|
+
<div>
|
|
1215
|
+
<div class="field-label">Metric</div>
|
|
1216
|
+
<div class="flex flex-wrap gap-2">
|
|
1217
|
+
${renderTokenUsageTrendMetricButtons()}
|
|
1218
|
+
</div>
|
|
1219
|
+
</div>
|
|
1220
|
+
</div>
|
|
1221
|
+
</aside>
|
|
994
1222
|
</div>
|
|
995
1223
|
`;
|
|
996
1224
|
}
|
|
997
1225
|
|
|
1226
|
+
function renderTokenUsageTrendMetricButtons() {
|
|
1227
|
+
const metrics = getTokenUsageTrendMetrics();
|
|
1228
|
+
return [
|
|
1229
|
+
{
|
|
1230
|
+
color: "var(--color-fg-lightest)",
|
|
1231
|
+
key: ALL_METRICS_VALUE,
|
|
1232
|
+
label: "All",
|
|
1233
|
+
},
|
|
1234
|
+
...metrics,
|
|
1235
|
+
]
|
|
1236
|
+
.map((metric) => {
|
|
1237
|
+
const isActive = state.tokenUsageTrendMetric === metric.key;
|
|
1238
|
+
return `
|
|
1239
|
+
<button
|
|
1240
|
+
type="button"
|
|
1241
|
+
data-trend-metric="${metric.key}"
|
|
1242
|
+
class="pill-button inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-semibold"
|
|
1243
|
+
style="background-color: ${isActive ? "rgba(37, 99, 235, 0.1)" : "var(--color-bg)"}; border-color: ${isActive ? "rgba(37, 99, 235, 0.28)" : "var(--color-bg-light-2)"}; color: ${isActive ? "var(--color-blue)" : "var(--color-fg-medium)"};"
|
|
1244
|
+
>
|
|
1245
|
+
<span class="h-2 w-2 rounded-full" style="background-color: ${metric.color};"></span>
|
|
1246
|
+
${escapeHtml(metric.label)}
|
|
1247
|
+
</button>
|
|
1248
|
+
`;
|
|
1249
|
+
})
|
|
1250
|
+
.join("");
|
|
1251
|
+
}
|
|
1252
|
+
|
|
998
1253
|
function renderTokenUsageTrendChart(dailySummary, selectedModel) {
|
|
999
1254
|
const days = dailySummary?.days || [];
|
|
1000
1255
|
const metrics = getTokenUsageTrendMetrics();
|
|
@@ -1155,33 +1410,14 @@
|
|
|
1155
1410
|
})
|
|
1156
1411
|
.join("");
|
|
1157
1412
|
const legend = [
|
|
1158
|
-
`<button type="button" data-trend-metric="${ALL_METRICS_VALUE}" class="inline-flex items-center border px-2 py-1 text-xs ${
|
|
1159
|
-
state.tokenUsageTrendMetric === ALL_METRICS_VALUE
|
|
1160
|
-
? "font-semibold"
|
|
1161
|
-
: ""
|
|
1162
|
-
}" style="background-color: ${
|
|
1163
|
-
state.tokenUsageTrendMetric === ALL_METRICS_VALUE
|
|
1164
|
-
? "var(--color-bg-light-1)"
|
|
1165
|
-
: "var(--color-bg)"
|
|
1166
|
-
}; border-color: var(--color-bg-light-2); color: var(--color-fg-light);">All</button>`,
|
|
1167
1413
|
...metrics.map(
|
|
1168
1414
|
(metric) => `
|
|
1169
|
-
<
|
|
1170
|
-
metric.key
|
|
1171
|
-
}" class="inline-flex items-center gap-1.5 border px-2 py-1 text-xs ${
|
|
1172
|
-
state.tokenUsageTrendMetric === metric.key
|
|
1173
|
-
? "font-semibold"
|
|
1174
|
-
: ""
|
|
1175
|
-
}" style="background-color: ${
|
|
1176
|
-
state.tokenUsageTrendMetric === metric.key
|
|
1177
|
-
? "var(--color-bg-light-1)"
|
|
1178
|
-
: "var(--color-bg)"
|
|
1179
|
-
}; border-color: var(--color-bg-light-2); color: var(--color-fg-light);">
|
|
1415
|
+
<span class="inline-flex items-center gap-1.5 text-xs font-medium" style="color: var(--color-gray);">
|
|
1180
1416
|
<span class="h-2 w-2 rounded-full" style="background-color: ${
|
|
1181
1417
|
metric.color
|
|
1182
1418
|
};"></span>
|
|
1183
1419
|
${escapeHtml(metric.label)}
|
|
1184
|
-
</
|
|
1420
|
+
</span>
|
|
1185
1421
|
`
|
|
1186
1422
|
),
|
|
1187
1423
|
].join("");
|
|
@@ -1212,7 +1448,7 @@
|
|
|
1212
1448
|
${lines}
|
|
1213
1449
|
${hitRects}
|
|
1214
1450
|
</svg>
|
|
1215
|
-
<div class="mt-2 flex flex-wrap gap-
|
|
1451
|
+
<div class="mt-2 flex flex-wrap gap-5">
|
|
1216
1452
|
${legend}
|
|
1217
1453
|
</div>
|
|
1218
1454
|
${activeValues}
|
|
@@ -1229,26 +1465,30 @@
|
|
|
1229
1465
|
|
|
1230
1466
|
function renderTokenUsageMetric(label, value, accentColor) {
|
|
1231
1467
|
return `
|
|
1232
|
-
<div class="
|
|
1233
|
-
<div class="
|
|
1468
|
+
<div class="metric-card">
|
|
1469
|
+
<div class="metric-label">
|
|
1470
|
+
<span class="metric-dot" style="background-color: ${accentColor};"></span>
|
|
1471
|
+
<span>${escapeHtml(label)}</span>
|
|
1472
|
+
</div>
|
|
1473
|
+
<div class="metric-value" style="color: var(--color-fg-lightest);">${renderTokenUsageValueLines(
|
|
1234
1474
|
typeof value === "number" ? formatNumber(value) : value
|
|
1235
1475
|
)}</div>
|
|
1236
|
-
<div class="
|
|
1237
|
-
label
|
|
1238
|
-
)}</div>
|
|
1476
|
+
<div class="metric-note">Current period</div>
|
|
1239
1477
|
</div>
|
|
1240
1478
|
`;
|
|
1241
1479
|
}
|
|
1242
1480
|
|
|
1243
1481
|
function renderTokenUsageCostMetric(label, value, accentColor) {
|
|
1244
1482
|
return `
|
|
1245
|
-
<div class="
|
|
1246
|
-
<div class="
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1483
|
+
<div class="metric-card">
|
|
1484
|
+
<div class="metric-label">
|
|
1485
|
+
<span class="metric-dot" style="background-color: ${accentColor};"></span>
|
|
1486
|
+
<span>${escapeHtml(label)}</span>
|
|
1487
|
+
</div>
|
|
1488
|
+
<div class="metric-value" style="color: var(--color-fg-lightest);">${renderTokenUsageValueLines(
|
|
1250
1489
|
value
|
|
1251
1490
|
)}</div>
|
|
1491
|
+
<div class="metric-note">Estimated total</div>
|
|
1252
1492
|
</div>
|
|
1253
1493
|
`;
|
|
1254
1494
|
}
|
|
@@ -1297,8 +1537,8 @@
|
|
|
1297
1537
|
|
|
1298
1538
|
return `
|
|
1299
1539
|
<div class="overflow-auto ${state.isTokenUsageLoading ? "opacity-60" : ""}">
|
|
1300
|
-
<table class="
|
|
1301
|
-
<thead
|
|
1540
|
+
<table class="data-table min-w-[860px] text-left">
|
|
1541
|
+
<thead>
|
|
1302
1542
|
<tr>
|
|
1303
1543
|
<th class="px-4 py-2 font-semibold">Model</th>
|
|
1304
1544
|
<th class="px-4 py-2 text-right font-semibold">Requests</th>
|
|
@@ -1376,8 +1616,8 @@
|
|
|
1376
1616
|
|
|
1377
1617
|
return `
|
|
1378
1618
|
<div class="overflow-auto ${state.isEventsLoading ? "opacity-60" : ""}">
|
|
1379
|
-
<table class="
|
|
1380
|
-
<thead
|
|
1619
|
+
<table class="data-table min-w-[1000px] text-left">
|
|
1620
|
+
<thead>
|
|
1381
1621
|
<tr>
|
|
1382
1622
|
<th class="px-4 py-2 font-semibold">Time</th>
|
|
1383
1623
|
<th class="px-4 py-2 font-semibold">User</th>
|
|
@@ -1406,8 +1646,7 @@
|
|
|
1406
1646
|
<button
|
|
1407
1647
|
type="button"
|
|
1408
1648
|
data-page-action="${action}"
|
|
1409
|
-
class="px-3 py-1.5
|
|
1410
|
-
style="background-color: var(--color-bg-darkest); border-color: var(--color-bg-light-2); color: var(--color-fg-light);"
|
|
1649
|
+
class="pill-button px-3 py-1.5 text-xs font-semibold"
|
|
1411
1650
|
${disabled ? "disabled" : ""}
|
|
1412
1651
|
>
|
|
1413
1652
|
${label}
|
|
@@ -1417,7 +1656,7 @@
|
|
|
1417
1656
|
|
|
1418
1657
|
function renderEmptyState(message) {
|
|
1419
1658
|
return `
|
|
1420
|
-
<div class="px-4 py-
|
|
1659
|
+
<div class="px-4 py-8 text-sm" style="color: var(--color-gray);">
|
|
1421
1660
|
${escapeHtml(message)}
|
|
1422
1661
|
</div>
|
|
1423
1662
|
`;
|
|
@@ -1430,7 +1669,7 @@
|
|
|
1430
1669
|
function renderSpinner() {
|
|
1431
1670
|
return `
|
|
1432
1671
|
<div class="flex justify-center items-center py-20">
|
|
1433
|
-
<div class="animate-spin h-12 w-12 rounded-full border-4 border-transparent border-t-4" style="border-top-color: var(--color-blue);"></div>
|
|
1672
|
+
<div class="animate-spin h-12 w-12 rounded-full border-4 border-transparent border-t-4" style="border-top-color: var(--color-blue); background-color: rgba(255, 255, 255, 0.65);"></div>
|
|
1434
1673
|
</div>`;
|
|
1435
1674
|
}
|
|
1436
1675
|
|
|
@@ -1442,8 +1681,8 @@
|
|
|
1442
1681
|
function renderError(message, title = "An Error Occurred") {
|
|
1443
1682
|
return `
|
|
1444
1683
|
<div
|
|
1445
|
-
class="p-3 border"
|
|
1446
|
-
style="background-color: rgba(
|
|
1684
|
+
class="p-3 border rounded-lg"
|
|
1685
|
+
style="background-color: rgba(220, 38, 38, 0.08); border-color: rgba(220, 38, 38, 0.22); color: var(--color-red);"
|
|
1447
1686
|
role="alert"
|
|
1448
1687
|
>
|
|
1449
1688
|
<div class="flex items-start">
|
|
@@ -1463,10 +1702,10 @@
|
|
|
1463
1702
|
*/
|
|
1464
1703
|
function renderWelcomeMessage() {
|
|
1465
1704
|
return `
|
|
1466
|
-
<div class="text-center py-16 px-4
|
|
1467
|
-
<i data-lucide="info" class="mx-auto h-10 w-10" style="color: var(--color-
|
|
1468
|
-
<h3 class="mt-
|
|
1469
|
-
<p class="mt-
|
|
1705
|
+
<div class="panel text-center py-16 px-4">
|
|
1706
|
+
<i data-lucide="info" class="mx-auto h-10 w-10" style="color: var(--color-blue);"></i>
|
|
1707
|
+
<h3 class="mt-3 text-lg font-bold" style="color: var(--color-fg-lightest);">Usage data is ready to load</h3>
|
|
1708
|
+
<p class="mt-2 text-sm" style="color: var(--color-gray);">Refresh the configured endpoint to view Copilot quotas, token totals, trends, and request events.</p>
|
|
1470
1709
|
</div>
|
|
1471
1710
|
`;
|
|
1472
1711
|
}
|
|
@@ -1812,36 +2051,5 @@
|
|
|
1812
2051
|
});
|
|
1813
2052
|
</script>
|
|
1814
2053
|
|
|
1815
|
-
<footer
|
|
1816
|
-
class="text-center py-4 text-xs"
|
|
1817
|
-
style="color: var(--color-gray-accent)"
|
|
1818
|
-
>
|
|
1819
|
-
<p>
|
|
1820
|
-
Vibe coded by
|
|
1821
|
-
<a
|
|
1822
|
-
href="https://gemini.google.com"
|
|
1823
|
-
target="_blank"
|
|
1824
|
-
rel="noopener noreferrer"
|
|
1825
|
-
class="underline transition-colors"
|
|
1826
|
-
style="color: var(--color-fg-dark)"
|
|
1827
|
-
onmouseover="this.style.color='var(--color-fg-light)'"
|
|
1828
|
-
onmouseout="this.style.color='var(--color-fg-dark)'"
|
|
1829
|
-
>
|
|
1830
|
-
Gemini
|
|
1831
|
-
</a>
|
|
1832
|
-
- Based on
|
|
1833
|
-
<a
|
|
1834
|
-
href="https://github.com/uheej0625/copilot-usage-viewer"
|
|
1835
|
-
target="_blank"
|
|
1836
|
-
rel="noopener noreferrer"
|
|
1837
|
-
class="underline transition-colors"
|
|
1838
|
-
style="color: var(--color-fg-dark)"
|
|
1839
|
-
onmouseover="this.style.color='var(--color-fg-light)'"
|
|
1840
|
-
onmouseout="this.style.color='var(--color-fg-dark)'"
|
|
1841
|
-
>
|
|
1842
|
-
copilot-usage-viewer</a
|
|
1843
|
-
>
|
|
1844
|
-
</p>
|
|
1845
|
-
</footer>
|
|
1846
2054
|
</body>
|
|
1847
2055
|
</html>
|