@jeffreycao/copilot-api 1.13.3 → 1.13.5
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-BXCLiIHf.js → server-Bsnz0tyL.js} +33 -4
- package/dist/server-Bsnz0tyL.js.map +1 -0
- package/dist/{start-DHjCOvZC.js → start-B3gNGDR_.js} +2 -2
- package/dist/{start-DHjCOvZC.js.map → start-B3gNGDR_.js.map} +1 -1
- package/package.json +1 -1
- package/pages/index.html +547 -355
- package/dist/server-BXCLiIHf.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
|
+
}
|
|
101
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
|
+
}
|
|
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
|
}
|
|
@@ -606,8 +783,8 @@
|
|
|
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,16 @@
|
|
|
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
|
+
`;
|
|
645
823
|
}
|
|
646
824
|
|
|
647
825
|
/**
|
|
@@ -665,26 +843,29 @@
|
|
|
665
843
|
if (unlimited) progressBarColor = "var(--color-blue)";
|
|
666
844
|
|
|
667
845
|
return `
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
846
|
+
<div class="metric-card">
|
|
847
|
+
<div class="flex items-center justify-between gap-3">
|
|
848
|
+
<div class="metric-label capitalize">
|
|
849
|
+
<span class="metric-dot" style="background-color: ${progressBarColor};"></span>
|
|
850
|
+
<span>${escapeHtml(title.replace(/_/g, " "))}</span>
|
|
851
|
+
</div>
|
|
852
|
+
${
|
|
853
|
+
unlimited
|
|
854
|
+
? `<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>`
|
|
855
|
+
: `<span class="text-xs font-semibold" style="color: var(--color-gray);">${percentUsed.toFixed(1)}% used</span>`
|
|
856
|
+
}
|
|
857
|
+
</div>
|
|
858
|
+
<div class="mt-5">
|
|
859
|
+
<div class="w-full progress-bar-bg h-2">
|
|
860
|
+
<div class="progress-bar-fg h-2" style="width: ${unlimited ? 100 : percentUsed}%; background-color: ${progressBarColor};"></div>
|
|
861
|
+
</div>
|
|
862
|
+
</div>
|
|
863
|
+
<div class="mt-4 flex items-center justify-between gap-4 text-xs font-medium" style="color: var(--color-gray);">
|
|
864
|
+
<span>${used} / ${unlimited ? "∞" : entitlement.toLocaleString()}</span>
|
|
865
|
+
<span>${unlimited ? "∞" : remaining.toLocaleString()} remaining</span>
|
|
866
|
+
</div>
|
|
867
|
+
</div>
|
|
868
|
+
`;
|
|
688
869
|
}
|
|
689
870
|
|
|
690
871
|
/**
|
|
@@ -694,7 +875,7 @@
|
|
|
694
875
|
*/
|
|
695
876
|
function formatObject(obj) {
|
|
696
877
|
if (obj === null || typeof obj !== "object") {
|
|
697
|
-
return `<span style="color: var(--color-green
|
|
878
|
+
return `<span style="color: var(--color-green);">${escapeHtml(JSON.stringify(obj))}</span>`;
|
|
698
879
|
}
|
|
699
880
|
|
|
700
881
|
return (
|
|
@@ -707,18 +888,18 @@
|
|
|
707
888
|
if (Array.isArray(value)) {
|
|
708
889
|
displayValue =
|
|
709
890
|
value.length > 0
|
|
710
|
-
? `<span style='color: var(--color-gray
|
|
711
|
-
: `<span style='color: var(--color-gray
|
|
891
|
+
? `<span style='color: var(--color-gray)'>[...${value.length} items]</span>`
|
|
892
|
+
: `<span style='color: var(--color-gray)'>[]</span>`;
|
|
712
893
|
} else if (typeof value === "object" && value !== null) {
|
|
713
894
|
displayValue = formatObject(value);
|
|
714
895
|
} else if (typeof value === "boolean") {
|
|
715
|
-
displayValue = `<span class="font-semibold" style="color: ${value ? "var(--color-green
|
|
896
|
+
displayValue = `<span class="font-semibold" style="color: ${value ? "var(--color-green)" : "var(--color-red)"}">${escapeHtml(String(value))}</span>`;
|
|
716
897
|
} else {
|
|
717
|
-
displayValue = `<span style="color: var(--color-blue
|
|
898
|
+
displayValue = `<span style="color: var(--color-blue);">${escapeHtml(JSON.stringify(value))}</span>`;
|
|
718
899
|
}
|
|
719
900
|
|
|
720
901
|
return `<div class="mt-1">
|
|
721
|
-
<span class="capitalize font-semibold" style="color: var(--color-fg-
|
|
902
|
+
<span class="capitalize font-semibold" style="color: var(--color-fg-lightest);">${formattedKey}:</span>
|
|
722
903
|
${typeof value === "object" && value !== null && !Array.isArray(value) ? displayValue : ` ${displayValue}`}
|
|
723
904
|
</div>`;
|
|
724
905
|
})
|
|
@@ -739,15 +920,16 @@
|
|
|
739
920
|
|
|
740
921
|
const formattedDetails = formatObject(data);
|
|
741
922
|
return `
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
923
|
+
<section id="detailed-data" class="dashboard-section">
|
|
924
|
+
<h2 class="section-heading">
|
|
925
|
+
<i data-lucide="file-text" class="h-5 w-5" style="color: var(--color-blue);"></i>
|
|
926
|
+
<span>Copilot Usage API Response</span>
|
|
927
|
+
</h2>
|
|
928
|
+
<div class="panel p-4 relative font-mono text-xs code-block overflow-auto" style="max-height: 420px;">
|
|
929
|
+
${formattedDetails}
|
|
930
|
+
</div>
|
|
931
|
+
</section>
|
|
932
|
+
`;
|
|
751
933
|
}
|
|
752
934
|
|
|
753
935
|
function renderTokenUsageSection() {
|
|
@@ -774,31 +956,32 @@
|
|
|
774
956
|
summary?.range || dailySummary?.range || eventsPage?.range || null;
|
|
775
957
|
const totalPages = eventsPage ? Math.max(eventsPage.total_pages, 1) : 1;
|
|
776
958
|
const eventsMeta = eventsPage
|
|
777
|
-
? `Page ${eventsPage.page} / ${totalPages}
|
|
959
|
+
? `Page ${eventsPage.page} / ${totalPages} - ${formatNumber(
|
|
778
960
|
eventsPage.total
|
|
779
961
|
)} events`
|
|
780
962
|
: "No detail page loaded yet.";
|
|
781
963
|
|
|
782
964
|
return `
|
|
783
|
-
<section id="token-usage" class="
|
|
784
|
-
<div class="mb-3 flex flex-col gap-2 sm:flex-row sm:items-
|
|
965
|
+
<section id="token-usage" class="dashboard-section">
|
|
966
|
+
<div class="mb-3 flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
|
|
785
967
|
<div>
|
|
786
|
-
<h2 class="
|
|
787
|
-
<i data-lucide="database"></i>
|
|
968
|
+
<h2 class="section-heading mb-1">
|
|
969
|
+
<i data-lucide="database" class="h-5 w-5" style="color: var(--color-blue);"></i>
|
|
970
|
+
<span>Token Usage</span>
|
|
788
971
|
</h2>
|
|
789
|
-
<p class="
|
|
972
|
+
<p class="section-subtitle mb-0">${escapeHtml(
|
|
790
973
|
renderTokenUsageRangeText(state.tokenUsagePeriod, activeRange)
|
|
791
974
|
)}</p>
|
|
792
975
|
</div>
|
|
793
|
-
<div class="flex flex-wrap items-center gap-2 text-xs" style="color: var(--color-gray
|
|
976
|
+
<div class="flex flex-wrap items-center gap-2 text-xs font-medium" style="color: var(--color-gray);">
|
|
794
977
|
${
|
|
795
978
|
state.isTokenUsageLoading
|
|
796
|
-
? "
|
|
979
|
+
? `<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
980
|
: ""
|
|
798
981
|
}
|
|
799
982
|
${
|
|
800
983
|
state.isEventsLoading
|
|
801
|
-
? "
|
|
984
|
+
? `<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
985
|
: ""
|
|
803
986
|
}
|
|
804
987
|
</div>
|
|
@@ -813,7 +996,7 @@
|
|
|
813
996
|
: ""
|
|
814
997
|
}
|
|
815
998
|
|
|
816
|
-
<div class="
|
|
999
|
+
<div class="metric-grid ${
|
|
817
1000
|
state.isTokenUsageLoading ? "opacity-60" : ""
|
|
818
1001
|
}">
|
|
819
1002
|
${renderTokenUsageMetric(
|
|
@@ -858,48 +1041,48 @@
|
|
|
858
1041
|
state.tokenUsageDailyError
|
|
859
1042
|
)}
|
|
860
1043
|
|
|
861
|
-
<div class="mt-4
|
|
862
|
-
<div class="
|
|
863
|
-
<div>
|
|
864
|
-
<
|
|
865
|
-
|
|
1044
|
+
<div class="mt-4 grid grid-cols-1 2xl:grid-cols-[minmax(0,1fr)_minmax(0,1.18fr)] gap-4">
|
|
1045
|
+
<div class="panel">
|
|
1046
|
+
<div class="panel-header">
|
|
1047
|
+
<div>
|
|
1048
|
+
<p class="panel-title">Model Breakdown</p>
|
|
1049
|
+
<p class="panel-meta">${summary ? `${formatNumber(summary.byModel.length)} models` : "No summary rows loaded."}</p>
|
|
1050
|
+
</div>
|
|
866
1051
|
</div>
|
|
1052
|
+
${renderTokenUsageModelBreakdown(summary)}
|
|
867
1053
|
</div>
|
|
868
|
-
${renderTokenUsageModelBreakdown(summary)}
|
|
869
|
-
</div>
|
|
870
1054
|
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
)}
|
|
1055
|
+
<div class="panel">
|
|
1056
|
+
<div class="panel-header flex-col items-start sm:flex-row sm:items-center">
|
|
1057
|
+
<div>
|
|
1058
|
+
<p class="panel-title">Request Events</p>
|
|
1059
|
+
<p class="panel-meta">${escapeHtml(eventsMeta)}</p>
|
|
1060
|
+
</div>
|
|
1061
|
+
<div class="flex items-center gap-2">
|
|
1062
|
+
${renderPaginationButton(
|
|
1063
|
+
"previous",
|
|
1064
|
+
"Previous",
|
|
1065
|
+
!eventsPage || state.isEventsLoading || eventsPage.page <= 1
|
|
1066
|
+
)}
|
|
1067
|
+
${renderPaginationButton(
|
|
1068
|
+
"next",
|
|
1069
|
+
"Next",
|
|
1070
|
+
!eventsPage
|
|
1071
|
+
|| state.isEventsLoading
|
|
1072
|
+
|| eventsPage.page >= totalPages
|
|
1073
|
+
)}
|
|
1074
|
+
</div>
|
|
892
1075
|
</div>
|
|
1076
|
+
${
|
|
1077
|
+
state.tokenUsageEventsError
|
|
1078
|
+
? `<div class="p-4">${renderError(
|
|
1079
|
+
state.tokenUsageEventsError,
|
|
1080
|
+
"Token usage details failed"
|
|
1081
|
+
)}</div>`
|
|
1082
|
+
: ""
|
|
1083
|
+
}
|
|
1084
|
+
${renderTokenUsageEventsTable(eventsPage)}
|
|
893
1085
|
</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
1086
|
</div>
|
|
904
1087
|
</section>
|
|
905
1088
|
`;
|
|
@@ -973,28 +1156,84 @@
|
|
|
973
1156
|
].join("");
|
|
974
1157
|
|
|
975
1158
|
return `
|
|
976
|
-
<div class="mt-4
|
|
977
|
-
<div class="
|
|
978
|
-
<
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1159
|
+
<div class="mt-4 grid grid-cols-1 xl:grid-cols-[minmax(0,1fr)_304px] gap-4">
|
|
1160
|
+
<div class="panel">
|
|
1161
|
+
<div class="panel-header">
|
|
1162
|
+
<div>
|
|
1163
|
+
<p class="panel-title">Token Usage Over Time</p>
|
|
1164
|
+
<p class="panel-meta">${escapeHtml(
|
|
1165
|
+
renderTokenUsageRangeText(
|
|
1166
|
+
state.tokenUsagePeriod,
|
|
1167
|
+
dailySummary?.range || null
|
|
1168
|
+
)
|
|
1169
|
+
)}</p>
|
|
1170
|
+
</div>
|
|
1171
|
+
</div>
|
|
1172
|
+
${
|
|
1173
|
+
error
|
|
1174
|
+
? `<div class="p-4">${renderError(
|
|
1175
|
+
error,
|
|
1176
|
+
"Token usage daily trend failed"
|
|
1177
|
+
)}</div>`
|
|
1178
|
+
: ""
|
|
1179
|
+
}
|
|
1180
|
+
${renderTokenUsageTrendChart(dailySummary, selectedModel)}
|
|
984
1181
|
</div>
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1182
|
+
|
|
1183
|
+
<aside class="panel">
|
|
1184
|
+
<div class="panel-header">
|
|
1185
|
+
<div>
|
|
1186
|
+
<p class="panel-title">Filters</p>
|
|
1187
|
+
<p class="panel-meta">Applied to the trend chart</p>
|
|
1188
|
+
</div>
|
|
1189
|
+
</div>
|
|
1190
|
+
<div class="p-4 space-y-4">
|
|
1191
|
+
<div>
|
|
1192
|
+
<label class="field-label">Model</label>
|
|
1193
|
+
<select
|
|
1194
|
+
data-token-usage-trend-model
|
|
1195
|
+
class="select-control input-focus text-sm"
|
|
1196
|
+
>${options}</select>
|
|
1197
|
+
</div>
|
|
1198
|
+
<div>
|
|
1199
|
+
<div class="field-label">Metric</div>
|
|
1200
|
+
<div class="flex flex-wrap gap-2">
|
|
1201
|
+
${renderTokenUsageTrendMetricButtons()}
|
|
1202
|
+
</div>
|
|
1203
|
+
</div>
|
|
1204
|
+
</div>
|
|
1205
|
+
</aside>
|
|
994
1206
|
</div>
|
|
995
1207
|
`;
|
|
996
1208
|
}
|
|
997
1209
|
|
|
1210
|
+
function renderTokenUsageTrendMetricButtons() {
|
|
1211
|
+
const metrics = getTokenUsageTrendMetrics();
|
|
1212
|
+
return [
|
|
1213
|
+
{
|
|
1214
|
+
color: "var(--color-fg-lightest)",
|
|
1215
|
+
key: ALL_METRICS_VALUE,
|
|
1216
|
+
label: "All",
|
|
1217
|
+
},
|
|
1218
|
+
...metrics,
|
|
1219
|
+
]
|
|
1220
|
+
.map((metric) => {
|
|
1221
|
+
const isActive = state.tokenUsageTrendMetric === metric.key;
|
|
1222
|
+
return `
|
|
1223
|
+
<button
|
|
1224
|
+
type="button"
|
|
1225
|
+
data-trend-metric="${metric.key}"
|
|
1226
|
+
class="pill-button inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-semibold"
|
|
1227
|
+
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)"};"
|
|
1228
|
+
>
|
|
1229
|
+
<span class="h-2 w-2 rounded-full" style="background-color: ${metric.color};"></span>
|
|
1230
|
+
${escapeHtml(metric.label)}
|
|
1231
|
+
</button>
|
|
1232
|
+
`;
|
|
1233
|
+
})
|
|
1234
|
+
.join("");
|
|
1235
|
+
}
|
|
1236
|
+
|
|
998
1237
|
function renderTokenUsageTrendChart(dailySummary, selectedModel) {
|
|
999
1238
|
const days = dailySummary?.days || [];
|
|
1000
1239
|
const metrics = getTokenUsageTrendMetrics();
|
|
@@ -1155,33 +1394,14 @@
|
|
|
1155
1394
|
})
|
|
1156
1395
|
.join("");
|
|
1157
1396
|
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
1397
|
...metrics.map(
|
|
1168
1398
|
(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);">
|
|
1399
|
+
<span class="inline-flex items-center gap-1.5 text-xs font-medium" style="color: var(--color-gray);">
|
|
1180
1400
|
<span class="h-2 w-2 rounded-full" style="background-color: ${
|
|
1181
1401
|
metric.color
|
|
1182
1402
|
};"></span>
|
|
1183
1403
|
${escapeHtml(metric.label)}
|
|
1184
|
-
</
|
|
1404
|
+
</span>
|
|
1185
1405
|
`
|
|
1186
1406
|
),
|
|
1187
1407
|
].join("");
|
|
@@ -1212,7 +1432,7 @@
|
|
|
1212
1432
|
${lines}
|
|
1213
1433
|
${hitRects}
|
|
1214
1434
|
</svg>
|
|
1215
|
-
<div class="mt-2 flex flex-wrap gap-
|
|
1435
|
+
<div class="mt-2 flex flex-wrap gap-5">
|
|
1216
1436
|
${legend}
|
|
1217
1437
|
</div>
|
|
1218
1438
|
${activeValues}
|
|
@@ -1229,26 +1449,30 @@
|
|
|
1229
1449
|
|
|
1230
1450
|
function renderTokenUsageMetric(label, value, accentColor) {
|
|
1231
1451
|
return `
|
|
1232
|
-
<div class="
|
|
1233
|
-
<div class="
|
|
1452
|
+
<div class="metric-card">
|
|
1453
|
+
<div class="metric-label">
|
|
1454
|
+
<span class="metric-dot" style="background-color: ${accentColor};"></span>
|
|
1455
|
+
<span>${escapeHtml(label)}</span>
|
|
1456
|
+
</div>
|
|
1457
|
+
<div class="metric-value" style="color: var(--color-fg-lightest);">${renderTokenUsageValueLines(
|
|
1234
1458
|
typeof value === "number" ? formatNumber(value) : value
|
|
1235
1459
|
)}</div>
|
|
1236
|
-
<div class="
|
|
1237
|
-
label
|
|
1238
|
-
)}</div>
|
|
1460
|
+
<div class="metric-note">Current period</div>
|
|
1239
1461
|
</div>
|
|
1240
1462
|
`;
|
|
1241
1463
|
}
|
|
1242
1464
|
|
|
1243
1465
|
function renderTokenUsageCostMetric(label, value, accentColor) {
|
|
1244
1466
|
return `
|
|
1245
|
-
<div class="
|
|
1246
|
-
<div class="
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1467
|
+
<div class="metric-card">
|
|
1468
|
+
<div class="metric-label">
|
|
1469
|
+
<span class="metric-dot" style="background-color: ${accentColor};"></span>
|
|
1470
|
+
<span>${escapeHtml(label)}</span>
|
|
1471
|
+
</div>
|
|
1472
|
+
<div class="metric-value" style="color: var(--color-fg-lightest);">${renderTokenUsageValueLines(
|
|
1250
1473
|
value
|
|
1251
1474
|
)}</div>
|
|
1475
|
+
<div class="metric-note">Estimated total</div>
|
|
1252
1476
|
</div>
|
|
1253
1477
|
`;
|
|
1254
1478
|
}
|
|
@@ -1297,8 +1521,8 @@
|
|
|
1297
1521
|
|
|
1298
1522
|
return `
|
|
1299
1523
|
<div class="overflow-auto ${state.isTokenUsageLoading ? "opacity-60" : ""}">
|
|
1300
|
-
<table class="
|
|
1301
|
-
<thead
|
|
1524
|
+
<table class="data-table min-w-[860px] text-left">
|
|
1525
|
+
<thead>
|
|
1302
1526
|
<tr>
|
|
1303
1527
|
<th class="px-4 py-2 font-semibold">Model</th>
|
|
1304
1528
|
<th class="px-4 py-2 text-right font-semibold">Requests</th>
|
|
@@ -1376,8 +1600,8 @@
|
|
|
1376
1600
|
|
|
1377
1601
|
return `
|
|
1378
1602
|
<div class="overflow-auto ${state.isEventsLoading ? "opacity-60" : ""}">
|
|
1379
|
-
<table class="
|
|
1380
|
-
<thead
|
|
1603
|
+
<table class="data-table min-w-[1000px] text-left">
|
|
1604
|
+
<thead>
|
|
1381
1605
|
<tr>
|
|
1382
1606
|
<th class="px-4 py-2 font-semibold">Time</th>
|
|
1383
1607
|
<th class="px-4 py-2 font-semibold">User</th>
|
|
@@ -1406,8 +1630,7 @@
|
|
|
1406
1630
|
<button
|
|
1407
1631
|
type="button"
|
|
1408
1632
|
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);"
|
|
1633
|
+
class="pill-button px-3 py-1.5 text-xs font-semibold"
|
|
1411
1634
|
${disabled ? "disabled" : ""}
|
|
1412
1635
|
>
|
|
1413
1636
|
${label}
|
|
@@ -1417,7 +1640,7 @@
|
|
|
1417
1640
|
|
|
1418
1641
|
function renderEmptyState(message) {
|
|
1419
1642
|
return `
|
|
1420
|
-
<div class="px-4 py-
|
|
1643
|
+
<div class="px-4 py-8 text-sm" style="color: var(--color-gray);">
|
|
1421
1644
|
${escapeHtml(message)}
|
|
1422
1645
|
</div>
|
|
1423
1646
|
`;
|
|
@@ -1430,7 +1653,7 @@
|
|
|
1430
1653
|
function renderSpinner() {
|
|
1431
1654
|
return `
|
|
1432
1655
|
<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>
|
|
1656
|
+
<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
1657
|
</div>`;
|
|
1435
1658
|
}
|
|
1436
1659
|
|
|
@@ -1442,8 +1665,8 @@
|
|
|
1442
1665
|
function renderError(message, title = "An Error Occurred") {
|
|
1443
1666
|
return `
|
|
1444
1667
|
<div
|
|
1445
|
-
class="p-3 border"
|
|
1446
|
-
style="background-color: rgba(
|
|
1668
|
+
class="p-3 border rounded-lg"
|
|
1669
|
+
style="background-color: rgba(220, 38, 38, 0.08); border-color: rgba(220, 38, 38, 0.22); color: var(--color-red);"
|
|
1447
1670
|
role="alert"
|
|
1448
1671
|
>
|
|
1449
1672
|
<div class="flex items-start">
|
|
@@ -1463,10 +1686,10 @@
|
|
|
1463
1686
|
*/
|
|
1464
1687
|
function renderWelcomeMessage() {
|
|
1465
1688
|
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-
|
|
1689
|
+
<div class="panel text-center py-16 px-4">
|
|
1690
|
+
<i data-lucide="info" class="mx-auto h-10 w-10" style="color: var(--color-blue);"></i>
|
|
1691
|
+
<h3 class="mt-3 text-lg font-bold" style="color: var(--color-fg-lightest);">Usage data is ready to load</h3>
|
|
1692
|
+
<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
1693
|
</div>
|
|
1471
1694
|
`;
|
|
1472
1695
|
}
|
|
@@ -1812,36 +2035,5 @@
|
|
|
1812
2035
|
});
|
|
1813
2036
|
</script>
|
|
1814
2037
|
|
|
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
2038
|
</body>
|
|
1847
2039
|
</html>
|