@jeffreycao/copilot-api 2.6.18 → 2.6.19
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/{auth-B1ZRVwUG.js → auth-DvEHwSn1.js} +2 -2
- package/dist/{auth-B1ZRVwUG.js.map → auth-DvEHwSn1.js.map} +1 -1
- package/dist/auth-EY18m6_Q.js +2 -0
- package/dist/main.js +2 -2
- package/dist/{models-DH6XfUL5.js → models-DNaL0mbS.js} +2 -2
- package/dist/{models-DH6XfUL5.js.map → models-DNaL0mbS.js.map} +1 -1
- package/dist/{server-BPn42Ogo.js → server-DNDv4jNx.js} +3 -3
- package/dist/{server-BPn42Ogo.js.map → server-DNDv4jNx.js.map} +1 -1
- package/dist/{start-DGRUGnBh.js → start-BIikhl49.js} +6 -8
- package/dist/{start-DGRUGnBh.js.map → start-BIikhl49.js.map} +1 -1
- package/dist/{token-BKpZGZR0.js → token-Datq4t9R.js} +2 -2
- package/dist/token-Datq4t9R.js.map +1 -0
- package/package.json +1 -1
- package/pages/index.html +471 -16
- package/dist/auth-YGms_d01.js +0 -2
- package/dist/token-BKpZGZR0.js.map +0 -1
package/pages/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Copilot API
|
|
6
|
+
<title>Copilot API Dashboard</title>
|
|
7
7
|
|
|
8
8
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
9
9
|
|
|
@@ -108,6 +108,128 @@
|
|
|
108
108
|
align-items: end;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
[hidden] {
|
|
112
|
+
display: none !important;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.page-tabs {
|
|
116
|
+
display: inline-flex;
|
|
117
|
+
gap: 4px;
|
|
118
|
+
margin-bottom: 14px;
|
|
119
|
+
padding: 4px;
|
|
120
|
+
border: 1px solid var(--color-bg-light-2);
|
|
121
|
+
border-radius: 10px;
|
|
122
|
+
background: rgba(255, 255, 255, 0.88);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.page-tab {
|
|
126
|
+
display: inline-flex;
|
|
127
|
+
align-items: center;
|
|
128
|
+
gap: 8px;
|
|
129
|
+
padding: 8px 14px;
|
|
130
|
+
border-radius: 7px;
|
|
131
|
+
color: var(--color-gray);
|
|
132
|
+
font-size: 14px;
|
|
133
|
+
font-weight: 700;
|
|
134
|
+
cursor: pointer;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.page-tab[aria-selected="true"] {
|
|
138
|
+
background: var(--color-blue);
|
|
139
|
+
color: #ffffff;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.page-tab:focus-visible,
|
|
143
|
+
.models-action:focus-visible {
|
|
144
|
+
outline: 2px solid var(--color-blue);
|
|
145
|
+
outline-offset: 3px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.control-surface.models-view {
|
|
149
|
+
grid-template-columns: minmax(320px, 1fr) minmax(220px, 320px) 132px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.models-toolbar {
|
|
153
|
+
flex-wrap: wrap;
|
|
154
|
+
padding: 14px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.models-toolbar .models-search {
|
|
158
|
+
width: min(340px, 100%);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.models-table-scroll {
|
|
162
|
+
overflow-x: auto;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.models-table {
|
|
166
|
+
width: 100%;
|
|
167
|
+
min-width: 640px;
|
|
168
|
+
border-collapse: collapse;
|
|
169
|
+
table-layout: fixed;
|
|
170
|
+
text-align: left;
|
|
171
|
+
font-size: 13px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.models-table th,
|
|
175
|
+
.models-table td {
|
|
176
|
+
padding: 14px;
|
|
177
|
+
border-bottom: 1px solid var(--color-bg-light-2);
|
|
178
|
+
overflow-wrap: anywhere;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.models-table th {
|
|
182
|
+
background: #f8fafc;
|
|
183
|
+
color: var(--color-gray);
|
|
184
|
+
font-size: 12px;
|
|
185
|
+
font-weight: 700;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.models-id {
|
|
189
|
+
display: flex;
|
|
190
|
+
align-items: center;
|
|
191
|
+
gap: 10px;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.models-id code {
|
|
195
|
+
min-width: 0;
|
|
196
|
+
color: var(--color-fg-lightest);
|
|
197
|
+
font-size: 12px;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.models-action {
|
|
201
|
+
display: inline-flex;
|
|
202
|
+
align-items: center;
|
|
203
|
+
gap: 4px;
|
|
204
|
+
flex-shrink: 0;
|
|
205
|
+
color: var(--color-blue);
|
|
206
|
+
font-size: 12px;
|
|
207
|
+
font-weight: 650;
|
|
208
|
+
cursor: pointer;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.models-action:hover {
|
|
212
|
+
color: var(--color-blue-accent);
|
|
213
|
+
text-decoration: underline;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.models-table .models-json-cell {
|
|
217
|
+
padding: 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.models-json {
|
|
221
|
+
margin: 0;
|
|
222
|
+
max-height: 480px;
|
|
223
|
+
overflow-y: auto;
|
|
224
|
+
padding: 16px;
|
|
225
|
+
background: #f8fafc;
|
|
226
|
+
color: var(--color-fg-medium);
|
|
227
|
+
font-size: 12px;
|
|
228
|
+
line-height: 1.6;
|
|
229
|
+
white-space: pre-wrap;
|
|
230
|
+
overflow-wrap: anywhere;
|
|
231
|
+
}
|
|
232
|
+
|
|
111
233
|
.field-label {
|
|
112
234
|
display: block;
|
|
113
235
|
margin-bottom: 6px;
|
|
@@ -360,6 +482,10 @@
|
|
|
360
482
|
.endpoint-field {
|
|
361
483
|
grid-column: 1 / -1;
|
|
362
484
|
}
|
|
485
|
+
|
|
486
|
+
.control-surface.models-view {
|
|
487
|
+
grid-template-columns: minmax(0, 1fr) 132px;
|
|
488
|
+
}
|
|
363
489
|
}
|
|
364
490
|
|
|
365
491
|
@media (max-width: 640px) {
|
|
@@ -371,10 +497,15 @@
|
|
|
371
497
|
display: block;
|
|
372
498
|
}
|
|
373
499
|
|
|
374
|
-
.control-surface
|
|
500
|
+
.control-surface,
|
|
501
|
+
.control-surface.models-view {
|
|
375
502
|
grid-template-columns: 1fr;
|
|
376
503
|
}
|
|
377
504
|
|
|
505
|
+
.models-toolbar .models-search {
|
|
506
|
+
width: 100%;
|
|
507
|
+
}
|
|
508
|
+
|
|
378
509
|
.endpoint-field {
|
|
379
510
|
grid-column: auto;
|
|
380
511
|
}
|
|
@@ -391,14 +522,47 @@
|
|
|
391
522
|
<header class="topbar">
|
|
392
523
|
<div class="topbar-row">
|
|
393
524
|
<div>
|
|
394
|
-
<h1 class="page-title">
|
|
395
|
-
<p class="page-subtitle">
|
|
525
|
+
<h1 class="page-title">Copilot API Dashboard</h1>
|
|
526
|
+
<p class="page-subtitle">Token usage and available models</p>
|
|
396
527
|
</div>
|
|
397
528
|
</div>
|
|
398
529
|
|
|
530
|
+
<div
|
|
531
|
+
id="page-tabs"
|
|
532
|
+
class="page-tabs"
|
|
533
|
+
role="tablist"
|
|
534
|
+
aria-label="Dashboard views"
|
|
535
|
+
>
|
|
536
|
+
<button
|
|
537
|
+
id="usage-tab"
|
|
538
|
+
type="button"
|
|
539
|
+
class="page-tab"
|
|
540
|
+
role="tab"
|
|
541
|
+
data-tab="usage"
|
|
542
|
+
aria-selected="true"
|
|
543
|
+
aria-controls="content-area"
|
|
544
|
+
>
|
|
545
|
+
<i data-lucide="bar-chart-3" class="h-4 w-4" aria-hidden="true"></i>
|
|
546
|
+
Token Usage
|
|
547
|
+
</button>
|
|
548
|
+
<button
|
|
549
|
+
id="models-tab"
|
|
550
|
+
type="button"
|
|
551
|
+
class="page-tab"
|
|
552
|
+
role="tab"
|
|
553
|
+
data-tab="models"
|
|
554
|
+
aria-selected="false"
|
|
555
|
+
aria-controls="content-area"
|
|
556
|
+
tabindex="-1"
|
|
557
|
+
>
|
|
558
|
+
<i data-lucide="boxes" class="h-4 w-4" aria-hidden="true"></i>
|
|
559
|
+
Models
|
|
560
|
+
</button>
|
|
561
|
+
</div>
|
|
562
|
+
|
|
399
563
|
<form id="endpoint-form" class="control-surface">
|
|
400
564
|
<div class="endpoint-field">
|
|
401
|
-
<label for="endpoint-url" class="field-label">
|
|
565
|
+
<label for="endpoint-url" class="field-label">Endpoint URL</label>
|
|
402
566
|
<input
|
|
403
567
|
type="text"
|
|
404
568
|
id="endpoint-url"
|
|
@@ -417,7 +581,7 @@
|
|
|
417
581
|
spellcheck="false"
|
|
418
582
|
/>
|
|
419
583
|
</div>
|
|
420
|
-
<div>
|
|
584
|
+
<div id="token-usage-period-field">
|
|
421
585
|
<label for="token-usage-period" class="field-label">Period</label>
|
|
422
586
|
<select
|
|
423
587
|
id="token-usage-period"
|
|
@@ -441,7 +605,12 @@
|
|
|
441
605
|
</p>
|
|
442
606
|
</header>
|
|
443
607
|
|
|
444
|
-
<main
|
|
608
|
+
<main
|
|
609
|
+
id="content-area"
|
|
610
|
+
class="content-area"
|
|
611
|
+
role="tabpanel"
|
|
612
|
+
aria-labelledby="usage-tab"
|
|
613
|
+
></main>
|
|
445
614
|
</div>
|
|
446
615
|
</div>
|
|
447
616
|
|
|
@@ -454,6 +623,12 @@
|
|
|
454
623
|
"token-usage-period"
|
|
455
624
|
);
|
|
456
625
|
const contentArea = document.getElementById("content-area");
|
|
626
|
+
const pageTabs = document.getElementById("page-tabs");
|
|
627
|
+
const tabButtons = [
|
|
628
|
+
document.getElementById("usage-tab"),
|
|
629
|
+
document.getElementById("models-tab"),
|
|
630
|
+
];
|
|
631
|
+
const periodField = document.getElementById("token-usage-period-field");
|
|
457
632
|
const fetchButton = document.getElementById("fetch-button");
|
|
458
633
|
const fetchButtonLabel = fetchButton.querySelector("span");
|
|
459
634
|
|
|
@@ -494,6 +669,13 @@
|
|
|
494
669
|
|
|
495
670
|
// --- State Management ---
|
|
496
671
|
const state = {
|
|
672
|
+
activeTab: "usage",
|
|
673
|
+
models: null,
|
|
674
|
+
modelsError: null,
|
|
675
|
+
isModelsLoading: false,
|
|
676
|
+
modelsConnectionKey: null,
|
|
677
|
+
modelsRequestId: 0,
|
|
678
|
+
modelsSearch: "",
|
|
497
679
|
isLoading: false,
|
|
498
680
|
isTokenUsageLoading: false,
|
|
499
681
|
isEventsLoading: false,
|
|
@@ -825,12 +1007,28 @@
|
|
|
825
1007
|
}
|
|
826
1008
|
|
|
827
1009
|
function updateControls() {
|
|
828
|
-
|
|
829
|
-
|
|
1010
|
+
const isLoading =
|
|
1011
|
+
state.activeTab === "models"
|
|
1012
|
+
? state.isModelsLoading
|
|
1013
|
+
: state.isLoading;
|
|
1014
|
+
fetchButton.disabled = isLoading;
|
|
1015
|
+
fetchButtonLabel.textContent = isLoading
|
|
830
1016
|
? "Refreshing..."
|
|
831
1017
|
: "Refresh";
|
|
832
1018
|
tokenUsagePeriodSelect.disabled =
|
|
833
1019
|
state.isLoading || state.isTokenUsageLoading;
|
|
1020
|
+
periodField.hidden = state.activeTab === "models";
|
|
1021
|
+
endpointForm.classList.toggle(
|
|
1022
|
+
"models-view",
|
|
1023
|
+
state.activeTab === "models"
|
|
1024
|
+
);
|
|
1025
|
+
contentArea.setAttribute("aria-labelledby", `${state.activeTab}-tab`);
|
|
1026
|
+
contentArea.setAttribute("aria-busy", String(isLoading));
|
|
1027
|
+
for (const button of tabButtons) {
|
|
1028
|
+
const selected = button.dataset.tab === state.activeTab;
|
|
1029
|
+
button.setAttribute("aria-selected", String(selected));
|
|
1030
|
+
button.tabIndex = selected ? 0 : -1;
|
|
1031
|
+
}
|
|
834
1032
|
}
|
|
835
1033
|
|
|
836
1034
|
function formatNumber(value) {
|
|
@@ -952,9 +1150,18 @@
|
|
|
952
1150
|
/**
|
|
953
1151
|
* Renders the entire UI based on the current state.
|
|
954
1152
|
*/
|
|
955
|
-
function render() {
|
|
1153
|
+
function render(view = state.activeTab) {
|
|
1154
|
+
if (view !== state.activeTab) {
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
956
1157
|
updateControls();
|
|
957
1158
|
|
|
1159
|
+
if (state.activeTab === "models") {
|
|
1160
|
+
contentArea.innerHTML = renderModelsSection();
|
|
1161
|
+
createIcons();
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
958
1165
|
if (state.isLoading) {
|
|
959
1166
|
contentArea.innerHTML = renderSpinner();
|
|
960
1167
|
createIcons();
|
|
@@ -995,6 +1202,223 @@
|
|
|
995
1202
|
createIcons();
|
|
996
1203
|
}
|
|
997
1204
|
|
|
1205
|
+
function getModelField(model, fields) {
|
|
1206
|
+
for (const field of fields) {
|
|
1207
|
+
if (typeof model?.[field] === "string" && model[field].trim()) {
|
|
1208
|
+
return model[field];
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
return "";
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
function getFilteredModels() {
|
|
1215
|
+
const query = state.modelsSearch.trim().toLowerCase();
|
|
1216
|
+
return (state.models || []).filter((model) => [
|
|
1217
|
+
getModelField(model, ["id"]),
|
|
1218
|
+
getModelField(model, ["display_name", "name"]),
|
|
1219
|
+
getModelField(model, ["owned_by", "vendor"]),
|
|
1220
|
+
].some((value) => value.toLowerCase().includes(query)));
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
function getModelsCountText() {
|
|
1224
|
+
const total = state.models?.length || 0;
|
|
1225
|
+
return state.modelsSearch.trim()
|
|
1226
|
+
? `${getFilteredModels().length} of ${total} models`
|
|
1227
|
+
: `${total} models`;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
function renderModelsSection() {
|
|
1231
|
+
return `
|
|
1232
|
+
<section class="panel" aria-label="Available models">
|
|
1233
|
+
<div class="panel-header models-toolbar">
|
|
1234
|
+
<div>
|
|
1235
|
+
<h2 class="panel-title">Models</h2>
|
|
1236
|
+
<p id="models-count" class="panel-meta">${getModelsCountText()}</p>
|
|
1237
|
+
</div>
|
|
1238
|
+
<input
|
|
1239
|
+
type="search"
|
|
1240
|
+
class="input-control input-focus models-search"
|
|
1241
|
+
data-model-search
|
|
1242
|
+
aria-label="Search models by ID, name or vendor"
|
|
1243
|
+
placeholder="Search by ID, name or vendor"
|
|
1244
|
+
value="${escapeHtml(state.modelsSearch)}"
|
|
1245
|
+
${state.isModelsLoading || !state.models ? "disabled" : ""}
|
|
1246
|
+
/>
|
|
1247
|
+
</div>
|
|
1248
|
+
<div id="models-results">${renderModelsResults()}</div>
|
|
1249
|
+
<p id="models-feedback" class="px-4 text-xs" role="status" aria-live="polite"></p>
|
|
1250
|
+
</section>
|
|
1251
|
+
`;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
function renderModelsResults() {
|
|
1255
|
+
if (state.isModelsLoading) {
|
|
1256
|
+
return renderSpinner();
|
|
1257
|
+
}
|
|
1258
|
+
if (state.modelsError) {
|
|
1259
|
+
return `<div class="p-4">${renderError(state.modelsError, "Could not load models")}</div>`;
|
|
1260
|
+
}
|
|
1261
|
+
if (!state.models) {
|
|
1262
|
+
return renderEmptyState("Refresh to load models for this endpoint.");
|
|
1263
|
+
}
|
|
1264
|
+
if (state.models.length === 0) {
|
|
1265
|
+
return renderEmptyState("This endpoint returned no models.");
|
|
1266
|
+
}
|
|
1267
|
+
const models = getFilteredModels();
|
|
1268
|
+
if (models.length === 0) {
|
|
1269
|
+
return renderEmptyState("No models match your search.");
|
|
1270
|
+
}
|
|
1271
|
+
return `
|
|
1272
|
+
<div class="models-table-scroll">
|
|
1273
|
+
<table class="models-table" aria-label="Model list">
|
|
1274
|
+
<thead>
|
|
1275
|
+
<tr>
|
|
1276
|
+
<th scope="col" style="width: 40%;">Model ID</th>
|
|
1277
|
+
<th scope="col" style="width: 28%;">Name</th>
|
|
1278
|
+
<th scope="col" style="width: 18%;">Vendor</th>
|
|
1279
|
+
<th scope="col" style="width: 14%;">Details</th>
|
|
1280
|
+
</tr>
|
|
1281
|
+
</thead>
|
|
1282
|
+
<tbody>
|
|
1283
|
+
${models.map((model, index) => {
|
|
1284
|
+
const id = getModelField(model, ["id"]);
|
|
1285
|
+
const name = getModelField(model, ["display_name", "name"]);
|
|
1286
|
+
const vendor = getModelField(model, ["owned_by", "vendor"]);
|
|
1287
|
+
return `
|
|
1288
|
+
<tr>
|
|
1289
|
+
<td>
|
|
1290
|
+
<div class="models-id">
|
|
1291
|
+
<code>${escapeHtml(id || "—")}</code>
|
|
1292
|
+
${id ? `<button type="button" class="models-action" data-model-copy="${escapeHtml(id)}" aria-label="Copy model ID ${escapeHtml(id)}"><i data-lucide="copy" class="h-3 w-3" aria-hidden="true"></i>Copy</button>` : ""}
|
|
1293
|
+
</div>
|
|
1294
|
+
</td>
|
|
1295
|
+
<td>${escapeHtml(name || "—")}</td>
|
|
1296
|
+
<td>${escapeHtml(vendor || "—")}</td>
|
|
1297
|
+
<td><button type="button" class="models-action" data-model-details="model-details-${index}" aria-controls="model-details-${index}" aria-expanded="false">View JSON</button></td>
|
|
1298
|
+
</tr>
|
|
1299
|
+
<tr id="model-details-${index}" hidden>
|
|
1300
|
+
<td colspan="4" class="models-json-cell"><pre class="models-json">${escapeHtml(JSON.stringify(model, null, 2))}</pre></td>
|
|
1301
|
+
</tr>
|
|
1302
|
+
`;
|
|
1303
|
+
}).join("")}
|
|
1304
|
+
</tbody>
|
|
1305
|
+
</table>
|
|
1306
|
+
</div>
|
|
1307
|
+
`;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
function getModelsConnectionKey() {
|
|
1311
|
+
return JSON.stringify([
|
|
1312
|
+
endpointUrlInput.value.trim(),
|
|
1313
|
+
apiKeyInput.value.trim(),
|
|
1314
|
+
]);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
function syncModelsConnection() {
|
|
1318
|
+
const key = getModelsConnectionKey();
|
|
1319
|
+
if (state.modelsConnectionKey !== key) {
|
|
1320
|
+
state.modelsConnectionKey = key;
|
|
1321
|
+
state.modelsRequestId += 1;
|
|
1322
|
+
state.models = null;
|
|
1323
|
+
state.modelsError = null;
|
|
1324
|
+
state.isModelsLoading = false;
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
function handleModelsConnectionChange() {
|
|
1329
|
+
syncModelsConnection();
|
|
1330
|
+
if (state.activeTab === "models") {
|
|
1331
|
+
render();
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
async function fetchModels() {
|
|
1336
|
+
syncEndpointCredential();
|
|
1337
|
+
syncModelsConnection();
|
|
1338
|
+
const connectionKey = state.modelsConnectionKey;
|
|
1339
|
+
const requestId = ++state.modelsRequestId;
|
|
1340
|
+
const isCurrentRequest = () =>
|
|
1341
|
+
requestId === state.modelsRequestId
|
|
1342
|
+
&& connectionKey === getModelsConnectionKey();
|
|
1343
|
+
state.models = null;
|
|
1344
|
+
state.modelsError = null;
|
|
1345
|
+
state.isModelsLoading = true;
|
|
1346
|
+
render("models");
|
|
1347
|
+
try {
|
|
1348
|
+
const endpoint = endpointUrlInput.value.trim();
|
|
1349
|
+
if (!endpoint) {
|
|
1350
|
+
throw new Error("Endpoint URL cannot be empty.");
|
|
1351
|
+
}
|
|
1352
|
+
const payload = await fetchJson(deriveEndpointUrl(endpoint, "models"));
|
|
1353
|
+
if (!Array.isArray(payload?.data)) {
|
|
1354
|
+
throw new Error("Model response must contain a data array.");
|
|
1355
|
+
}
|
|
1356
|
+
if (isCurrentRequest()) {
|
|
1357
|
+
state.models = payload.data;
|
|
1358
|
+
}
|
|
1359
|
+
} catch (error) {
|
|
1360
|
+
if (isCurrentRequest()) {
|
|
1361
|
+
state.modelsError = getErrorMessage(error);
|
|
1362
|
+
}
|
|
1363
|
+
} finally {
|
|
1364
|
+
if (isCurrentRequest()) {
|
|
1365
|
+
state.isModelsLoading = false;
|
|
1366
|
+
render("models");
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
function activateTab(tab) {
|
|
1372
|
+
if (tab !== "usage" && tab !== "models") {
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
syncEndpointCredential();
|
|
1376
|
+
syncModelsConnection();
|
|
1377
|
+
state.activeTab = tab;
|
|
1378
|
+
render();
|
|
1379
|
+
if (tab === "models" && !state.models && !state.isModelsLoading) {
|
|
1380
|
+
void fetchModels();
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
function handleTabsKeydown(event) {
|
|
1385
|
+
const keys = ["ArrowLeft", "ArrowRight", "Home", "End"];
|
|
1386
|
+
if (!keys.includes(event.key)) {
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
event.preventDefault();
|
|
1390
|
+
const index = state.activeTab === "usage" ? 0 : 1;
|
|
1391
|
+
const nextIndex = event.key === "Home" ? 0
|
|
1392
|
+
: event.key === "End" ? 1
|
|
1393
|
+
: 1 - index;
|
|
1394
|
+
activateTab(tabButtons[nextIndex].dataset.tab);
|
|
1395
|
+
tabButtons[nextIndex].focus();
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
function handleModelsSearch(event) {
|
|
1399
|
+
if (!event.target.matches("[data-model-search]")) {
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
state.modelsSearch = event.target.value;
|
|
1403
|
+
document.getElementById("models-results").innerHTML =
|
|
1404
|
+
renderModelsResults();
|
|
1405
|
+
document.getElementById("models-count").textContent = getModelsCountText();
|
|
1406
|
+
document.getElementById("models-feedback").textContent = "";
|
|
1407
|
+
createIcons();
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
async function copyModelId(id) {
|
|
1411
|
+
const feedback = document.getElementById("models-feedback");
|
|
1412
|
+
try {
|
|
1413
|
+
await navigator.clipboard.writeText(id);
|
|
1414
|
+
feedback.textContent = "Model ID copied.";
|
|
1415
|
+
} catch {
|
|
1416
|
+
feedback.textContent =
|
|
1417
|
+
"Could not copy. Select the model ID and copy it manually.";
|
|
1418
|
+
}
|
|
1419
|
+
feedback.classList.add("py-3");
|
|
1420
|
+
}
|
|
1421
|
+
|
|
998
1422
|
/**
|
|
999
1423
|
* Renders the "Usage Quotas" section with progress bars.
|
|
1000
1424
|
* @param {object} snapshots - The quota_snapshots object from the API response.
|
|
@@ -2072,7 +2496,7 @@
|
|
|
2072
2496
|
state.tokenUsageEventsError = getErrorMessage(error);
|
|
2073
2497
|
} finally {
|
|
2074
2498
|
state.isLoading = false;
|
|
2075
|
-
render();
|
|
2499
|
+
render("usage");
|
|
2076
2500
|
}
|
|
2077
2501
|
}
|
|
2078
2502
|
|
|
@@ -2161,7 +2585,7 @@
|
|
|
2161
2585
|
state.tokenUsageEventsError = getErrorMessage(error);
|
|
2162
2586
|
} finally {
|
|
2163
2587
|
state.isTokenUsageLoading = false;
|
|
2164
|
-
render();
|
|
2588
|
+
render("usage");
|
|
2165
2589
|
}
|
|
2166
2590
|
}
|
|
2167
2591
|
|
|
@@ -2193,7 +2617,7 @@
|
|
|
2193
2617
|
}
|
|
2194
2618
|
} finally {
|
|
2195
2619
|
state.isEventsLoading = false;
|
|
2196
|
-
render();
|
|
2620
|
+
render("usage");
|
|
2197
2621
|
}
|
|
2198
2622
|
}
|
|
2199
2623
|
|
|
@@ -2208,7 +2632,11 @@
|
|
|
2208
2632
|
syncEndpointCredential();
|
|
2209
2633
|
storeApiKey(apiKeyInput.value, endpointUrlInput.value.trim());
|
|
2210
2634
|
syncUrlState();
|
|
2211
|
-
|
|
2635
|
+
if (state.activeTab === "models") {
|
|
2636
|
+
void fetchModels();
|
|
2637
|
+
} else {
|
|
2638
|
+
void fetchData();
|
|
2639
|
+
}
|
|
2212
2640
|
}
|
|
2213
2641
|
|
|
2214
2642
|
function handlePeriodChange() {
|
|
@@ -2220,7 +2648,21 @@
|
|
|
2220
2648
|
if (!(event.target instanceof Element)) {
|
|
2221
2649
|
return;
|
|
2222
2650
|
}
|
|
2223
|
-
|
|
2651
|
+
const copyButton = event.target.closest("[data-model-copy]");
|
|
2652
|
+
if (copyButton) {
|
|
2653
|
+
void copyModelId(copyButton.dataset.modelCopy);
|
|
2654
|
+
return;
|
|
2655
|
+
}
|
|
2656
|
+
const detailsButton = event.target.closest("[data-model-details]");
|
|
2657
|
+
if (detailsButton) {
|
|
2658
|
+
const details = document.getElementById(
|
|
2659
|
+
detailsButton.dataset.modelDetails
|
|
2660
|
+
);
|
|
2661
|
+
details.hidden = !details.hidden;
|
|
2662
|
+
detailsButton.setAttribute("aria-expanded", String(!details.hidden));
|
|
2663
|
+
detailsButton.textContent = details.hidden ? "View JSON" : "Hide JSON";
|
|
2664
|
+
return;
|
|
2665
|
+
}
|
|
2224
2666
|
const trendMetric = event.target.closest("[data-trend-metric]");
|
|
2225
2667
|
if (trendMetric) {
|
|
2226
2668
|
state.tokenUsageTrendMetric =
|
|
@@ -2293,16 +2735,29 @@
|
|
|
2293
2735
|
*/
|
|
2294
2736
|
function init() {
|
|
2295
2737
|
endpointForm.addEventListener("submit", handleFormSubmit);
|
|
2738
|
+
pageTabs.addEventListener("click", (event) => {
|
|
2739
|
+
const button = event.target.closest("[data-tab]");
|
|
2740
|
+
if (button) {
|
|
2741
|
+
activateTab(button.dataset.tab);
|
|
2742
|
+
}
|
|
2743
|
+
});
|
|
2744
|
+
pageTabs.addEventListener("keydown", handleTabsKeydown);
|
|
2296
2745
|
tokenUsagePeriodSelect.addEventListener("change", handlePeriodChange);
|
|
2297
2746
|
apiKeyInput.addEventListener("input", () => {
|
|
2298
2747
|
storeApiKey(
|
|
2299
2748
|
apiKeyInput.value,
|
|
2300
2749
|
endpointUrlInput.value.trim()
|
|
2301
2750
|
);
|
|
2751
|
+
handleModelsConnectionChange();
|
|
2752
|
+
});
|
|
2753
|
+
endpointUrlInput.addEventListener("input", handleModelsConnectionChange);
|
|
2754
|
+
endpointUrlInput.addEventListener("change", () => {
|
|
2755
|
+
syncEndpointCredential();
|
|
2756
|
+
handleModelsConnectionChange();
|
|
2302
2757
|
});
|
|
2303
|
-
endpointUrlInput.addEventListener("change", syncEndpointCredential);
|
|
2304
2758
|
contentArea.addEventListener("click", handleContentAreaClick);
|
|
2305
2759
|
contentArea.addEventListener("change", handleContentAreaChange);
|
|
2760
|
+
contentArea.addEventListener("input", handleModelsSearch);
|
|
2306
2761
|
|
|
2307
2762
|
const urlParams = new URLSearchParams(window.location.search);
|
|
2308
2763
|
const periodFromUrl = normalizePeriod(urlParams.get("period"));
|
package/dist/auth-YGms_d01.js
DELETED