@jeffreycao/copilot-api 2.7.18 → 2.7.20

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/pages/index.html CHANGED
@@ -306,6 +306,44 @@
306
306
  font-weight: 750;
307
307
  }
308
308
 
309
+ .collapse-toggle {
310
+ display: inline-flex;
311
+ align-items: center;
312
+ gap: 6px;
313
+ padding: 0;
314
+ border: 0;
315
+ background: none;
316
+ color: inherit;
317
+ font: inherit;
318
+ text-align: left;
319
+ cursor: pointer;
320
+ }
321
+
322
+ .collapse-toggle:focus-visible {
323
+ outline: 2px solid var(--color-blue);
324
+ outline-offset: 2px;
325
+ border-radius: 4px;
326
+ }
327
+
328
+ .collapse-chevron {
329
+ width: 16px;
330
+ height: 16px;
331
+ flex-shrink: 0;
332
+ color: var(--color-gray);
333
+ }
334
+
335
+ .collapse-toggle[aria-expanded="false"] .collapse-chevron {
336
+ transform: rotate(-90deg);
337
+ }
338
+
339
+ .collapse-body[hidden] {
340
+ display: none !important;
341
+ }
342
+
343
+ .panel-header:has(+ .collapse-body[hidden]) {
344
+ border-bottom-color: transparent;
345
+ }
346
+
309
347
  .section-subtitle {
310
348
  margin-top: -4px;
311
349
  margin-bottom: 8px;
@@ -700,6 +738,7 @@
700
738
  tokenUsageTrendDay: null,
701
739
  tokenUsageTrendMetric: ALL_METRICS_VALUE,
702
740
  tokenUsageTrendModel: ALL_MODELS_VALUE,
741
+ collapsedSections: {},
703
742
  };
704
743
 
705
744
  // --- Rendering Logic ---
@@ -1230,6 +1269,110 @@
1230
1269
  createIcons();
1231
1270
  }
1232
1271
 
1272
+ // --- Collapsible Sections ---
1273
+ const COLLAPSED_SECTIONS_STORAGE_KEY =
1274
+ "copilot-api:usage-viewer:collapsed";
1275
+ const COLLAPSIBLE_SECTION_KEYS = new Set([
1276
+ "tokenUsage",
1277
+ "modelBreakdown",
1278
+ "sessionBreakdown",
1279
+ "requestEvents",
1280
+ "copilotQuotas",
1281
+ "usageApiResponse",
1282
+ ]);
1283
+
1284
+ function readCollapsedSections() {
1285
+ try {
1286
+ const raw = window.localStorage.getItem(
1287
+ COLLAPSED_SECTIONS_STORAGE_KEY
1288
+ );
1289
+ const parsed = raw ? JSON.parse(raw) : null;
1290
+ const collapsed = {};
1291
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1292
+ for (const key of COLLAPSIBLE_SECTION_KEYS) {
1293
+ if (parsed[key] === true) {
1294
+ collapsed[key] = true;
1295
+ }
1296
+ }
1297
+ }
1298
+ return collapsed;
1299
+ } catch (error) {
1300
+ console.warn(
1301
+ "Could not read collapsed sections:",
1302
+ getErrorMessage(error)
1303
+ );
1304
+ return {};
1305
+ }
1306
+ }
1307
+
1308
+ function writeCollapsedSections() {
1309
+ try {
1310
+ window.localStorage.setItem(
1311
+ COLLAPSED_SECTIONS_STORAGE_KEY,
1312
+ JSON.stringify(state.collapsedSections)
1313
+ );
1314
+ } catch (error) {
1315
+ console.warn(
1316
+ "Could not store collapsed sections:",
1317
+ getErrorMessage(error)
1318
+ );
1319
+ }
1320
+ }
1321
+
1322
+ function isSectionCollapsed(key) {
1323
+ return state.collapsedSections[key] === true;
1324
+ }
1325
+
1326
+ function getCollapseBodyId(key) {
1327
+ return `collapse-body-${key}`;
1328
+ }
1329
+
1330
+ /**
1331
+ * Renders the toggle button (chevron + label) shown in a section header.
1332
+ * @param {string} key - A COLLAPSIBLE_SECTION_KEYS entry.
1333
+ * @param {string} labelHtml - Trusted HTML for the toggle label.
1334
+ */
1335
+ function renderCollapseToggle(key, labelHtml) {
1336
+ return `<button type="button" class="collapse-toggle" data-collapse-toggle="${key}" aria-expanded="${!isSectionCollapsed(key)}" aria-controls="${getCollapseBodyId(key)}"><i data-lucide="chevron-down" class="collapse-chevron"></i>${labelHtml}</button>`;
1337
+ }
1338
+
1339
+ /**
1340
+ * Opens a section body wrapper that is hidden while the section is
1341
+ * collapsed. Pair with renderCollapseBodyEnd() for multi-part bodies.
1342
+ * @param {string} key - A COLLAPSIBLE_SECTION_KEYS entry.
1343
+ * @param {string} className - Optional extra classes for the wrapper.
1344
+ */
1345
+ function renderCollapseBodyStart(key, className = "") {
1346
+ return `<div id="${getCollapseBodyId(key)}" class="collapse-body ${className}" ${isSectionCollapsed(key) ? "hidden" : ""}>`;
1347
+ }
1348
+
1349
+ function renderCollapseBodyEnd() {
1350
+ return "</div>";
1351
+ }
1352
+
1353
+ function renderCollapseBody(key, bodyHtml, className = "") {
1354
+ return `${renderCollapseBodyStart(key, className)}${bodyHtml}${renderCollapseBodyEnd()}`;
1355
+ }
1356
+
1357
+ function toggleCollapsedSection(button) {
1358
+ const key = button.dataset.collapseToggle;
1359
+ if (!COLLAPSIBLE_SECTION_KEYS.has(key)) {
1360
+ return;
1361
+ }
1362
+ const collapsed = !isSectionCollapsed(key);
1363
+ if (collapsed) {
1364
+ state.collapsedSections[key] = true;
1365
+ } else {
1366
+ delete state.collapsedSections[key];
1367
+ }
1368
+ const body = document.getElementById(getCollapseBodyId(key));
1369
+ if (body) {
1370
+ body.hidden = collapsed;
1371
+ }
1372
+ button.setAttribute("aria-expanded", String(!collapsed));
1373
+ writeCollapsedSections();
1374
+ }
1375
+
1233
1376
  function getModelField(model, fields) {
1234
1377
  for (const field of fields) {
1235
1378
  if (typeof model?.[field] === "string" && model[field].trim()) {
@@ -1464,12 +1607,12 @@
1464
1607
  return `
1465
1608
  <section id="usage-quotas" class="dashboard-section">
1466
1609
  <h2 class="section-heading">
1467
- <i data-lucide="gauge" class="h-5 w-5" style="color: var(--color-blue);"></i>
1468
- <span>Copilot Quotas</span>
1610
+ ${renderCollapseToggle(
1611
+ "copilotQuotas",
1612
+ `<i data-lucide="gauge" class="h-5 w-5" style="color: var(--color-blue);"></i><span>Copilot Quotas</span>`
1613
+ )}
1469
1614
  </h2>
1470
- <div class="metric-grid">
1471
- ${quotaCards}
1472
- </div>
1615
+ ${renderCollapseBody("copilotQuotas", quotaCards, "metric-grid")}
1473
1616
  </section>
1474
1617
  `;
1475
1618
  }
@@ -1482,10 +1625,15 @@
1482
1625
  return `
1483
1626
  <section id="usage-quotas" class="dashboard-section">
1484
1627
  <h2 class="section-heading">
1485
- <i data-lucide="gauge" class="h-5 w-5" style="color: var(--color-blue);"></i>
1486
- <span>Copilot Quotas</span>
1628
+ ${renderCollapseToggle(
1629
+ "copilotQuotas",
1630
+ `<i data-lucide="gauge" class="h-5 w-5" style="color: var(--color-blue);"></i><span>Copilot Quotas</span>`
1631
+ )}
1487
1632
  </h2>
1488
- ${renderError(state.error, "Usage request failed")}
1633
+ ${renderCollapseBody(
1634
+ "copilotQuotas",
1635
+ renderError(state.error, "Usage request failed")
1636
+ )}
1489
1637
  </section>
1490
1638
  `;
1491
1639
  }
@@ -1620,12 +1768,17 @@
1620
1768
  return `
1621
1769
  <section id="detailed-data" class="dashboard-section">
1622
1770
  <h2 class="section-heading">
1623
- <i data-lucide="file-text" class="h-5 w-5" style="color: var(--color-blue);"></i>
1624
- <span>Copilot Usage API Response</span>
1771
+ ${renderCollapseToggle(
1772
+ "usageApiResponse",
1773
+ `<i data-lucide="file-text" class="h-5 w-5" style="color: var(--color-blue);"></i><span>Copilot Usage API Response</span>`
1774
+ )}
1625
1775
  </h2>
1626
- <div class="panel p-4 relative font-mono text-xs code-block overflow-auto" style="max-height: 420px;">
1627
- ${formattedDetails}
1628
- </div>
1776
+ ${renderCollapseBody(
1777
+ "usageApiResponse",
1778
+ `<div class="panel p-4 relative font-mono text-xs code-block overflow-auto" style="max-height: 420px;">
1779
+ ${formattedDetails}
1780
+ </div>`
1781
+ )}
1629
1782
  </section>
1630
1783
  `;
1631
1784
  }
@@ -1665,8 +1818,10 @@
1665
1818
  <div class="mb-2 flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
1666
1819
  <div>
1667
1820
  <h2 class="section-heading mb-1">
1668
- <i data-lucide="database" class="h-5 w-5" style="color: var(--color-blue);"></i>
1669
- <span>Token Usage</span>
1821
+ ${renderCollapseToggle(
1822
+ "tokenUsage",
1823
+ `<i data-lucide="database" class="h-5 w-5" style="color: var(--color-blue);"></i><span>Token Usage</span>`
1824
+ )}
1670
1825
  </h2>
1671
1826
  <p class="section-subtitle mb-0">${escapeHtml(
1672
1827
  renderTokenUsageRangeText(state.tokenUsagePeriod, activeRange)
@@ -1686,6 +1841,7 @@
1686
1841
  </div>
1687
1842
  </div>
1688
1843
 
1844
+ ${renderCollapseBodyStart("tokenUsage")}
1689
1845
  ${
1690
1846
  state.tokenUsageSummaryError
1691
1847
  ? renderError(
@@ -1791,17 +1947,20 @@
1791
1947
  <div class="panel">
1792
1948
  <div class="panel-header">
1793
1949
  <div>
1794
- <p class="panel-title">Model Breakdown</p>
1950
+ <p class="panel-title">${renderCollapseToggle("modelBreakdown", "<span>Model Breakdown</span>")}</p>
1795
1951
  <p class="panel-meta">${summary ? `${formatNumber(summary.byModel.length)} models` : "No summary rows loaded."}</p>
1796
1952
  </div>
1797
1953
  </div>
1798
- ${renderTokenUsageModelBreakdown(summary)}
1954
+ ${renderCollapseBody(
1955
+ "modelBreakdown",
1956
+ renderTokenUsageModelBreakdown(summary)
1957
+ )}
1799
1958
  </div>
1800
1959
 
1801
1960
  <div class="panel">
1802
1961
  <div class="panel-header">
1803
1962
  <div>
1804
- <p class="panel-title">Session Breakdown</p>
1963
+ <p class="panel-title">${renderCollapseToggle("sessionBreakdown", "<span>Session Breakdown</span>")}</p>
1805
1964
  <p class="panel-meta">${sessionsPage ? `Page ${sessionsPage.page} / ${sessionsPage.total_pages} - ${formatNumber(sessionsPage.total)} sessions` : "No session rows loaded."}</p>
1806
1965
  </div>
1807
1966
  <div class="flex items-center gap-2">
@@ -1809,6 +1968,7 @@
1809
1968
  <button type="button" data-session-page="1" class="pill-button px-3 py-1.5 text-xs font-semibold" ${!sessionsPage || state.isSessionsLoading || sessionsPage.page >= sessionsPage.total_pages ? "disabled" : ""}>Next</button>
1810
1969
  </div>
1811
1970
  </div>
1971
+ ${renderCollapseBodyStart("sessionBreakdown")}
1812
1972
  ${
1813
1973
  state.tokenUsageSessionsError
1814
1974
  ? `<div class="p-4">${renderError(
@@ -1818,12 +1978,13 @@
1818
1978
  : ""
1819
1979
  }
1820
1980
  ${renderTokenUsageModelBreakdown({ bySession: sessionsPage?.items }, "session")}
1981
+ ${renderCollapseBodyEnd()}
1821
1982
  </div>
1822
1983
 
1823
1984
  <div class="panel">
1824
1985
  <div class="panel-header flex-col items-start sm:flex-row sm:items-center">
1825
1986
  <div>
1826
- <p class="panel-title">Request Events</p>
1987
+ <p class="panel-title">${renderCollapseToggle("requestEvents", "<span>Request Events</span>")}</p>
1827
1988
  <p class="panel-meta">${escapeHtml(eventsMeta)}</p>
1828
1989
  </div>
1829
1990
  <div class="flex items-center gap-2">
@@ -1841,6 +2002,7 @@
1841
2002
  )}
1842
2003
  </div>
1843
2004
  </div>
2005
+ ${renderCollapseBodyStart("requestEvents")}
1844
2006
  ${
1845
2007
  state.tokenUsageEventsError
1846
2008
  ? `<div class="p-4">${renderError(
@@ -1850,8 +2012,10 @@
1850
2012
  : ""
1851
2013
  }
1852
2014
  ${renderTokenUsageEventsTable(eventsPage)}
2015
+ ${renderCollapseBodyEnd()}
1853
2016
  </div>
1854
2017
  </div>
2018
+ ${renderCollapseBodyEnd()}
1855
2019
  </section>
1856
2020
  `;
1857
2021
  }
@@ -2817,6 +2981,11 @@
2817
2981
  void copyModelId(copyButton.dataset.modelCopy);
2818
2982
  return;
2819
2983
  }
2984
+ const collapseToggle = event.target.closest("[data-collapse-toggle]");
2985
+ if (collapseToggle) {
2986
+ toggleCollapsedSection(collapseToggle);
2987
+ return;
2988
+ }
2820
2989
  const detailsButton = event.target.closest("[data-model-details]");
2821
2990
  if (detailsButton) {
2822
2991
  const details = document.getElementById(
@@ -2945,6 +3114,7 @@
2945
3114
  endpointUrlInput.value = initialEndpoint;
2946
3115
  migrateLegacyApiKey(initialEndpoint);
2947
3116
  syncEndpointCredential();
3117
+ state.collapsedSections = readCollapsedSections();
2948
3118
 
2949
3119
  if (endpointFromUrl) {
2950
3120
  void fetchData();
@@ -1,2 +0,0 @@
1
- import { t as auth } from "./auth-BI9j5Vof.js";
2
- export { auth };