@iasiv5/dsh-quota-watch 0.0.11 → 0.0.13
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 +2 -2
- package/lib/client.js +204 -18
- package/package.json +1 -1
- package/src/client.mjs +211 -20
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## 功能
|
|
8
8
|
|
|
9
|
-
- 在侧栏底部区块的最顶部(框架渲染的插件入口与设置行之上)显示一张常显摘要卡(无标题、无刷新按钮):GLM 与 GitHub Copilot 各占一行,统一显示**已用百分比**(Copilot 为 `100 − percent_remaining` 的单值反转),已用超过 80% 进度条转橙、超过 95% 转红;点击行首的 GLM / Copilot
|
|
9
|
+
- 在侧栏底部区块的最顶部(框架渲染的插件入口与设置行之上)显示一张常显摘要卡(无标题、无刷新按钮):GLM 与 GitHub Copilot 各占一行,统一显示**已用百分比**(Copilot 为 `100 − percent_remaining` 的单值反转),已用超过 80% 进度条转橙、超过 95% 转红;点击行首的 GLM / Copilot 名称立即触发一次宿主探测(等同原刷新按钮),点击行其余区域展开详情。侧栏收起时改为 36×36px 无框按钮与 16px 配额仪表盘图标,和设置按钮对齐;颜色使用 DSH 主题语义变量,自动适配浅色/深色皮肤。点击图标打开 GLM / Copilot 概览,可继续查看额度详情;不显示常驻提醒点。
|
|
10
10
|
- GLM 摘要行固定显示 5 小时窗口已用百分比,行尾等宽小字为当日 token 用量;点击行在侧栏右侧弹出详情面板(半透明毛玻璃底,深浅主题均可读,不改变卡片高度):今日 Tokens / 调用次数双大数字、窗口区为五列对齐网格(名称 | `·` | `已用`/♾️ | 百分比 | 同字号弱色「(重置: …)」,♾️ 与 `已用` 同列左对齐,弱色括注整列左对齐)、虚线分隔的分模型用量行(名称左对齐、token 数右对齐)。窗口按 MCP(月)→ 5 小时 → 每月 → 每周固定排序;接口未下发周窗口时显示「每周额度 · ♾️(无限)」占位,真实周窗口数据一旦下发即自动替换占位。同一时间至多展开一个详情;点击面板外或按 Esc 关闭。
|
|
11
11
|
- GitHub Copilot 摘要行仅在 GitHub 返回有限、有效的 `remaining` 且带 `percent_remaining` 时显示;行尾等宽小字显示本期 credits 消耗的紧凑值。点击在侧栏右侧弹出精简详情(标签左对齐、数值右对齐的三行):可用额度、本期已用(credits 消耗 + 弱化的已用百分比)、重置时间。`credits_used` 绝不用于推算余额,缺失有效百分比时该行不渲染。
|
|
12
12
|
- 卡片顶部不显示全局更新时间;仅在等待首个快照或刷新失败时显示一行状态文字(读取中 / 刷新失败 / 查询失败)。
|
|
@@ -48,4 +48,4 @@ dsh plugin --profile web add @iasiv5/dsh-quota-watch
|
|
|
48
48
|
|
|
49
49
|
## English summary
|
|
50
50
|
|
|
51
|
-
DSH Quota Watch is a personal DSH Web sidebar card for GLM Coding Plan windows and GitHub Copilot's provider-reported quota snapshot. It resolves credentials on every host-side probe, sends only normalized values to the browser, keeps no usage history, and never infers a Copilot balance from credits used. The Copilot endpoint is undocumented and may change; invalid or unlimited quota snapshots are shown as unavailable rather than guessed.
|
|
51
|
+
DSH Quota Watch is a personal DSH Web sidebar card for GLM Coding Plan windows and GitHub Copilot's provider-reported quota snapshot. It resolves credentials on every host-side probe, sends only normalized values to the browser, keeps no usage history, and never infers a Copilot balance from credits used. When the sidebar is collapsed, a 36px control with a 16px quota-gauge icon replaces the text card; clicking it opens provider summaries and details. The icon uses DSH semantic theme tokens and has no permanent border or alert badge. The Copilot endpoint is undocumented and may change; invalid or unlimited quota snapshots are shown as unavailable rather than guessed.
|
package/lib/client.js
CHANGED
|
@@ -89,6 +89,9 @@ var FETCH_TIMEOUT_MS = 15e3;
|
|
|
89
89
|
var COPY = {
|
|
90
90
|
zh: {
|
|
91
91
|
title: "\u5957\u9910\u76D1\u63A7",
|
|
92
|
+
openDetails: "\u67E5\u770B\u8BE6\u60C5",
|
|
93
|
+
backToOverview: "\u8FD4\u56DE\u989D\u5EA6\u6982\u89C8",
|
|
94
|
+
providerNames: { glm: "GLM", copilot: "Copilot" },
|
|
92
95
|
refresh: "\u5237\u65B0",
|
|
93
96
|
loading: "\u8BFB\u53D6\u4E2D",
|
|
94
97
|
failed: "\u67E5\u8BE2\u5931\u8D25",
|
|
@@ -103,7 +106,7 @@ var COPY = {
|
|
|
103
106
|
todayCalls: "\u8C03\u7528\u6B21\u6570",
|
|
104
107
|
weekUnlimited: "\u6BCF\u5468\u989D\u5EA6",
|
|
105
108
|
unlimited: "\u267E\uFE0F",
|
|
106
|
-
unlimitedNote: "\uFF08\u65E0\u9650\uFF09",
|
|
109
|
+
unlimitedNote: "\uFF08 \u65E0\u9650 \uFF09",
|
|
107
110
|
windows: {
|
|
108
111
|
"5h": "5 \u5C0F\u65F6\u989D\u5EA6",
|
|
109
112
|
week: "\u6BCF\u5468\u989D\u5EA6",
|
|
@@ -113,6 +116,9 @@ var COPY = {
|
|
|
113
116
|
},
|
|
114
117
|
en: {
|
|
115
118
|
title: "Quota Watch",
|
|
119
|
+
openDetails: "View details",
|
|
120
|
+
backToOverview: "Back to quota overview",
|
|
121
|
+
providerNames: { glm: "GLM", copilot: "Copilot" },
|
|
116
122
|
refresh: "Refresh",
|
|
117
123
|
loading: "Loading",
|
|
118
124
|
failed: "Query failed",
|
|
@@ -127,7 +133,7 @@ var COPY = {
|
|
|
127
133
|
todayCalls: "Calls",
|
|
128
134
|
weekUnlimited: "Weekly quota",
|
|
129
135
|
unlimited: "\u267E\uFE0F",
|
|
130
|
-
unlimitedNote: " (unlimited)",
|
|
136
|
+
unlimitedNote: " ( unlimited )",
|
|
131
137
|
windows: {
|
|
132
138
|
"5h": "5-hour quota",
|
|
133
139
|
week: "Weekly quota",
|
|
@@ -159,10 +165,26 @@ var STYLE_TEXT = `
|
|
|
159
165
|
.dqw-chev { font-size: 9px; line-height: 14px; opacity: .6; flex: none; width: 10px; text-align: center; }
|
|
160
166
|
.dqw-stale-mark { flex: none; font-size: 9px; line-height: 14px; color: var(--dsw-alias-label-warning, #b46900); cursor: help; }
|
|
161
167
|
.dqw-errtext { font-size: 10px; line-height: 14px; opacity: .7; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
168
|
+
.dqw-rail-trigger { display: none; flex: none; align-items: center; justify-content: center; width: 36px; height: 36px; margin: 0; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--dsw-alias-label-primary, inherit); cursor: pointer; transition: background-color .12s, color .12s; }
|
|
169
|
+
.dqw-rail-trigger svg { display: block; width: 16px; height: 16px; }
|
|
170
|
+
.dqw-rail-trigger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); color: var(--dsw-alias-label-primary, inherit); }
|
|
171
|
+
.dqw-rail-trigger:active:not(:disabled), .dqw-rail-trigger[aria-expanded="true"] { background: var(--dsw-alias-interactive-bg-active, rgba(128,128,128,.18)); color: var(--dsw-alias-label-primary, inherit); }
|
|
172
|
+
.dqw-rail-trigger:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #5b8def); outline-offset: 2px; }
|
|
173
|
+
[data-dsh-frame][data-sidebar-collapsed] [data-dsh-quota-watch-card] { display: flex; align-items: center; justify-content: center; width: 36px; margin: 0 0 12px; }
|
|
174
|
+
[data-dsh-frame][data-sidebar-collapsed] [data-dsh-quota-watch-card] > .dqw-card { display: none; }
|
|
175
|
+
[data-dsh-frame][data-sidebar-collapsed] [data-dsh-quota-watch-card] > .dqw-rail-trigger { display: inline-flex; }
|
|
162
176
|
.dqw-pop { position: fixed; z-index: 2147483000; min-width: 166px; max-width: 320px; padding: 10px 12px; border: 1px solid var(--dsw-alias-border-secondary, rgba(128,128,128,.35)); border-radius: 10px; background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f)); background: color-mix(in srgb, var(--dsw-alias-bg-base, #1f1f1f) 86%, transparent); -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3); box-shadow: 0 8px 24px rgba(0,0,0,.25); color: var(--dsw-alias-label-primary, inherit); display: flex; flex-direction: column; gap: 8px; font: inherit; }
|
|
163
177
|
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .dqw-pop { background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f)); } }
|
|
164
178
|
.dqw-pop[hidden] { display: none; }
|
|
165
179
|
.dqw-pop *, .dqw-pop *::before, .dqw-pop *::after { box-sizing: border-box; }
|
|
180
|
+
.dqw-pop-title { margin: 0; font-size: 11px; font-weight: 600; line-height: 16px; }
|
|
181
|
+
.dqw-pop-back { align-self: flex-start; margin: 0; padding: 3px 5px; border: 0; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, inherit); font: inherit; font-size: 10.5px; line-height: 14px; cursor: pointer; }
|
|
182
|
+
.dqw-pop-back:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); color: var(--dsw-alias-label-primary, inherit); }
|
|
183
|
+
.dqw-overview-list { display: flex; flex-direction: column; gap: 2px; }
|
|
184
|
+
.dqw-overview-row { display: flex; align-items: center; width: 100%; gap: 6px; margin: 0; padding: 5px 4px; border: 0; border-radius: 8px; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
|
|
185
|
+
.dqw-overview-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); }
|
|
186
|
+
.dqw-overview-row:focus-visible { outline: 1px solid var(--dsw-alias-brand-primary, #5b8def); outline-offset: -1px; }
|
|
187
|
+
.dqw-overview-error { display: flex; align-items: center; gap: 6px; padding: 5px 4px; }
|
|
166
188
|
.dqw-bignums { display: flex; gap: 6px; }
|
|
167
189
|
.dqw-big { flex: 1; border: 1px solid var(--dsw-alias-border-secondary, rgba(128,128,128,.25)); border-radius: 8px; padding: 5px 8px; }
|
|
168
190
|
.dqw-big .dqw-big-value { font-size: 15px; font-weight: 650; line-height: 19px; font-variant-numeric: tabular-nums; }
|
|
@@ -218,6 +240,32 @@ function text(doc, tag, className, value) {
|
|
|
218
240
|
node.textContent = String(value ?? "");
|
|
219
241
|
return node;
|
|
220
242
|
}
|
|
243
|
+
function quotaIcon(doc) {
|
|
244
|
+
const namespace = "http://www.w3.org/2000/svg";
|
|
245
|
+
const svg = doc.createElementNS(namespace, "svg");
|
|
246
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
247
|
+
svg.setAttribute("width", "16");
|
|
248
|
+
svg.setAttribute("height", "16");
|
|
249
|
+
svg.setAttribute("fill", "none");
|
|
250
|
+
svg.setAttribute("stroke", "currentColor");
|
|
251
|
+
svg.setAttribute("stroke-width", "1.7");
|
|
252
|
+
svg.setAttribute("stroke-linecap", "round");
|
|
253
|
+
svg.setAttribute("stroke-linejoin", "round");
|
|
254
|
+
svg.setAttribute("aria-hidden", "true");
|
|
255
|
+
svg.setAttribute("focusable", "false");
|
|
256
|
+
const arc = doc.createElementNS(namespace, "path");
|
|
257
|
+
arc.setAttribute("d", "M4 17a8 8 0 0 1 16 0");
|
|
258
|
+
const needle = doc.createElementNS(namespace, "path");
|
|
259
|
+
needle.setAttribute("d", "m12 17 3.2-5.2");
|
|
260
|
+
const hub = doc.createElementNS(namespace, "circle");
|
|
261
|
+
hub.setAttribute("cx", "12");
|
|
262
|
+
hub.setAttribute("cy", "17");
|
|
263
|
+
hub.setAttribute("r", "1");
|
|
264
|
+
hub.setAttribute("fill", "currentColor");
|
|
265
|
+
hub.setAttribute("stroke", "none");
|
|
266
|
+
svg.append(arc, needle, hub);
|
|
267
|
+
return svg;
|
|
268
|
+
}
|
|
221
269
|
function kvRow(doc, label, value) {
|
|
222
270
|
const row = doc.createElement("div");
|
|
223
271
|
row.className = "dqw-kv";
|
|
@@ -289,15 +337,26 @@ function mountQuotaCard({
|
|
|
289
337
|
const card = doc.createElement("section");
|
|
290
338
|
card.className = "dqw-card";
|
|
291
339
|
card.setAttribute("aria-label", copy.title);
|
|
340
|
+
const railTrigger = doc.createElement("button");
|
|
341
|
+
railTrigger.type = "button";
|
|
342
|
+
railTrigger.className = "dqw-rail-trigger";
|
|
343
|
+
railTrigger.title = copy.title;
|
|
344
|
+
railTrigger.setAttribute("aria-label", copy.title);
|
|
345
|
+
railTrigger.setAttribute("aria-haspopup", "dialog");
|
|
346
|
+
railTrigger.setAttribute("aria-controls", "dqw-quota-watch-pop");
|
|
347
|
+
railTrigger.setAttribute("aria-expanded", "false");
|
|
348
|
+
railTrigger.append(quotaIcon(doc));
|
|
292
349
|
const body = doc.createElement("div");
|
|
293
350
|
body.className = "dqw-body";
|
|
294
351
|
const lastUpdated = text(doc, "p", "dqw-meta", copy.loading);
|
|
295
352
|
lastUpdated.dataset.role = "updated";
|
|
296
353
|
body.append(lastUpdated);
|
|
297
354
|
card.append(body);
|
|
298
|
-
container.append(style, card);
|
|
355
|
+
container.append(style, card, railTrigger);
|
|
299
356
|
const pop = doc.createElement("div");
|
|
300
357
|
pop.className = "dqw-pop";
|
|
358
|
+
pop.id = "dqw-quota-watch-pop";
|
|
359
|
+
pop.setAttribute("role", "dialog");
|
|
301
360
|
pop.dataset.dshQuotaWatchPop = "";
|
|
302
361
|
pop.hidden = true;
|
|
303
362
|
doc.body.append(pop);
|
|
@@ -307,6 +366,7 @@ function mountQuotaCard({
|
|
|
307
366
|
let disposed = false;
|
|
308
367
|
let queuedPlace = false;
|
|
309
368
|
let openKey;
|
|
369
|
+
let popOrigin = "row";
|
|
310
370
|
const fillClass = (percent) => percent > 95 ? "dqw-bar-fill danger" : percent > 80 ? "dqw-bar-fill warn" : "dqw-bar-fill";
|
|
311
371
|
const renderProvider = (key, model) => {
|
|
312
372
|
if (model === null) return null;
|
|
@@ -353,8 +413,73 @@ function mountQuotaCard({
|
|
|
353
413
|
row.append(text(doc, "span", "dqw-chev", openKey === key ? "\u25BE" : "\u25B8"));
|
|
354
414
|
return [row];
|
|
355
415
|
};
|
|
356
|
-
|
|
416
|
+
const renderOverviewProvider = (key, model) => {
|
|
417
|
+
if (model === null) return null;
|
|
418
|
+
const label = text(doc, "span", "dqw-label", model.label);
|
|
419
|
+
if (model.kind === "error") {
|
|
420
|
+
const row2 = doc.createElement("div");
|
|
421
|
+
row2.className = "dqw-overview-error";
|
|
422
|
+
row2.append(label, text(doc, "span", "dqw-errtext", model.error ? `${copy.failed}: ${model.error}` : copy.failed));
|
|
423
|
+
return row2;
|
|
424
|
+
}
|
|
425
|
+
const row = doc.createElement("button");
|
|
426
|
+
row.type = "button";
|
|
427
|
+
row.className = "dqw-overview-row";
|
|
428
|
+
row.dataset.dshQuotaWatchPopProvider = key;
|
|
429
|
+
row.setAttribute("aria-expanded", "false");
|
|
430
|
+
row.setAttribute("aria-label", `${model.label} \xB7 ${typeof model.percent === "number" ? formatPercent(model.percent, locale) : "\u2014"} \xB7 ${copy.openDetails}`);
|
|
431
|
+
row.append(label);
|
|
432
|
+
if (typeof model.percent === "number" && Number.isFinite(model.percent)) {
|
|
433
|
+
const bar = doc.createElement("span");
|
|
434
|
+
bar.className = "dqw-bar";
|
|
435
|
+
bar.setAttribute("role", "progressbar");
|
|
436
|
+
bar.setAttribute("aria-valuemin", "0");
|
|
437
|
+
bar.setAttribute("aria-valuemax", "100");
|
|
438
|
+
bar.setAttribute("aria-valuenow", String(Math.max(0, Math.min(100, model.percent))));
|
|
439
|
+
const fill = text(doc, "span", fillClass(model.percent));
|
|
440
|
+
fill.style.width = `${Math.max(0, Math.min(100, model.percent))}%`;
|
|
441
|
+
bar.append(fill);
|
|
442
|
+
row.append(bar);
|
|
443
|
+
}
|
|
444
|
+
if (model.stale) {
|
|
445
|
+
const mark = text(doc, "span", "dqw-stale-mark", "\u26A0");
|
|
446
|
+
mark.dataset.dshQuotaWatchStale = "";
|
|
447
|
+
mark.title = model.error ?? copy.stale;
|
|
448
|
+
row.append(mark);
|
|
449
|
+
}
|
|
450
|
+
row.append(text(doc, "span", "dqw-pct", typeof model.percent === "number" ? formatPercent(model.percent, locale) : "\u2014"));
|
|
451
|
+
row.append(text(doc, "span", "dqw-chev", "\u25B8"));
|
|
452
|
+
return row;
|
|
453
|
+
};
|
|
454
|
+
const renderOverview = () => {
|
|
455
|
+
pop.replaceChildren(text(doc, "p", "dqw-pop-title", copy.title));
|
|
456
|
+
const providers = snapshot?.providers ?? [];
|
|
457
|
+
const glm = providers.find((provider) => provider?.key === "glm");
|
|
458
|
+
const copilot = providers.find((provider) => provider?.key === "copilot");
|
|
459
|
+
const rows = [
|
|
460
|
+
renderOverviewProvider("glm", glmRowModel(glm)),
|
|
461
|
+
renderOverviewProvider("copilot", copilotRowModel(copilot))
|
|
462
|
+
].filter(Boolean);
|
|
463
|
+
if (rows.length === 0) {
|
|
464
|
+
pop.append(text(doc, "p", "dqw-muted", lastUpdated.textContent || copy.loading));
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
const list = doc.createElement("div");
|
|
468
|
+
list.className = "dqw-overview-list";
|
|
469
|
+
list.append(...rows);
|
|
470
|
+
pop.append(list);
|
|
471
|
+
};
|
|
472
|
+
function renderDetail(detail, key, provider, fromRail = false) {
|
|
357
473
|
detail.replaceChildren();
|
|
474
|
+
if (fromRail) {
|
|
475
|
+
const back = doc.createElement("button");
|
|
476
|
+
back.type = "button";
|
|
477
|
+
back.className = "dqw-pop-back";
|
|
478
|
+
back.dataset.action = "back-to-overview";
|
|
479
|
+
back.setAttribute("aria-label", copy.backToOverview);
|
|
480
|
+
back.textContent = copy.backToOverview;
|
|
481
|
+
detail.append(back, text(doc, "p", "dqw-pop-title", copy.providerNames[key]));
|
|
482
|
+
}
|
|
358
483
|
if (provider?.status === "stale") {
|
|
359
484
|
detail.append(text(doc, "p", "dqw-error", `${copy.stale}${provider.error ? `: ${provider.error}` : ""}`));
|
|
360
485
|
}
|
|
@@ -410,7 +535,7 @@ function mountQuotaCard({
|
|
|
410
535
|
wins.append(text(doc, "span", "dqw-win-mid", "\xB7"));
|
|
411
536
|
wins.append(text(doc, "span", "dqw-win-used", copy.windowUsed));
|
|
412
537
|
wins.append(text(doc, "span", "dqw-win-val", percent !== void 0 ? formatPercent(percent, locale) : "\u2014"));
|
|
413
|
-
wins.append(text(doc, "span", "dqw-kv-sub", reset ? `\uFF08${copy.reset}: ${reset}\uFF09` : ""));
|
|
538
|
+
wins.append(text(doc, "span", "dqw-kv-sub", reset ? `\uFF08 ${copy.reset}: ${reset} \uFF09` : ""));
|
|
414
539
|
}
|
|
415
540
|
detail.append(wins);
|
|
416
541
|
if (Array.isArray(usage?.models) && usage.models.length > 0) {
|
|
@@ -451,9 +576,9 @@ function mountQuotaCard({
|
|
|
451
576
|
}
|
|
452
577
|
const positionPop = () => {
|
|
453
578
|
if (pop.hidden || openKey === void 0) return;
|
|
454
|
-
const
|
|
455
|
-
if (!
|
|
456
|
-
const rect =
|
|
579
|
+
const anchor = popOrigin === "rail" ? railTrigger : body.querySelector(`[data-dsh-quota-watch-row="${openKey}"]`);
|
|
580
|
+
if (!anchor) return;
|
|
581
|
+
const rect = anchor.getBoundingClientRect();
|
|
457
582
|
const viewWidth = win.innerWidth ?? 1024;
|
|
458
583
|
const viewHeight = win.innerHeight ?? 768;
|
|
459
584
|
const width = pop.offsetWidth || 240;
|
|
@@ -467,24 +592,35 @@ function mountQuotaCard({
|
|
|
467
592
|
pop.style.top = `${Math.round(top)}px`;
|
|
468
593
|
};
|
|
469
594
|
const syncPop = () => {
|
|
595
|
+
const fromRail = popOrigin === "rail" && openKey !== void 0 && !disposed;
|
|
596
|
+
railTrigger.setAttribute("aria-expanded", String(fromRail));
|
|
470
597
|
if (openKey === void 0 || disposed) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
598
|
+
pop.hidden = true;
|
|
599
|
+
pop.replaceChildren();
|
|
600
|
+
delete pop.dataset.dshQuotaWatchDetail;
|
|
601
|
+
delete pop.dataset.dshQuotaWatchOverview;
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (openKey === "overview") {
|
|
605
|
+
renderOverview();
|
|
606
|
+
delete pop.dataset.dshQuotaWatchDetail;
|
|
607
|
+
pop.dataset.dshQuotaWatchOverview = "";
|
|
608
|
+
pop.setAttribute("aria-label", copy.title);
|
|
609
|
+
pop.hidden = false;
|
|
610
|
+
positionPop();
|
|
476
611
|
return;
|
|
477
612
|
}
|
|
478
613
|
const provider = (snapshot?.providers ?? []).find((item) => item?.key === openKey);
|
|
479
614
|
const row = body.querySelector(`[data-dsh-quota-watch-row="${openKey}"]`);
|
|
480
|
-
if (!provider || !row || !row.hasAttribute("data-expandable")) {
|
|
615
|
+
if (!provider || !fromRail && (!row || !row.hasAttribute("data-expandable"))) {
|
|
481
616
|
openKey = void 0;
|
|
482
617
|
syncPop();
|
|
483
618
|
return;
|
|
484
619
|
}
|
|
485
|
-
renderDetail(pop, openKey, provider);
|
|
620
|
+
renderDetail(pop, openKey, provider, fromRail);
|
|
486
621
|
pop.dataset.dshQuotaWatchDetail = openKey;
|
|
487
|
-
pop.
|
|
622
|
+
delete pop.dataset.dshQuotaWatchOverview;
|
|
623
|
+
pop.setAttribute("aria-label", fromRail ? `${copy.providerNames[openKey]} \xB7 ${copy.title}` : copy.providerNames[openKey]);
|
|
488
624
|
pop.hidden = false;
|
|
489
625
|
positionPop();
|
|
490
626
|
};
|
|
@@ -542,8 +678,27 @@ function mountQuotaCard({
|
|
|
542
678
|
if (!disposed) place();
|
|
543
679
|
});
|
|
544
680
|
};
|
|
545
|
-
|
|
546
|
-
|
|
681
|
+
let sidebarCollapsed = Boolean(doc.querySelector("[data-dsh-frame][data-sidebar-collapsed]"));
|
|
682
|
+
const syncSidebarMode = () => {
|
|
683
|
+
const nextCollapsed = Boolean(doc.querySelector("[data-dsh-frame][data-sidebar-collapsed]"));
|
|
684
|
+
if (nextCollapsed === sidebarCollapsed) return;
|
|
685
|
+
sidebarCollapsed = nextCollapsed;
|
|
686
|
+
if (openKey !== void 0) {
|
|
687
|
+
openKey = void 0;
|
|
688
|
+
popOrigin = "row";
|
|
689
|
+
syncPop();
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
const observer = new win.MutationObserver(() => {
|
|
693
|
+
syncSidebarMode();
|
|
694
|
+
schedulePlace();
|
|
695
|
+
});
|
|
696
|
+
observer.observe(doc.body, {
|
|
697
|
+
childList: true,
|
|
698
|
+
subtree: true,
|
|
699
|
+
attributes: true,
|
|
700
|
+
attributeFilter: ["data-sidebar-collapsed"]
|
|
701
|
+
});
|
|
547
702
|
place();
|
|
548
703
|
const startPolling = () => {
|
|
549
704
|
if (timer !== void 0 || doc.visibilityState === "hidden" || disposed) return;
|
|
@@ -561,9 +716,34 @@ function mountQuotaCard({
|
|
|
561
716
|
else startPolling();
|
|
562
717
|
};
|
|
563
718
|
const toggleRow = (key) => {
|
|
719
|
+
popOrigin = "row";
|
|
564
720
|
openKey = openKey === key ? void 0 : key;
|
|
565
721
|
renderAll();
|
|
566
722
|
};
|
|
723
|
+
const onRailClick = () => {
|
|
724
|
+
if (popOrigin === "rail" && openKey !== void 0) {
|
|
725
|
+
openKey = void 0;
|
|
726
|
+
popOrigin = "row";
|
|
727
|
+
} else {
|
|
728
|
+
openKey = "overview";
|
|
729
|
+
popOrigin = "rail";
|
|
730
|
+
}
|
|
731
|
+
syncPop();
|
|
732
|
+
};
|
|
733
|
+
const onPopClick = (event) => {
|
|
734
|
+
if (event.target?.closest?.('[data-action="back-to-overview"]')) {
|
|
735
|
+
openKey = "overview";
|
|
736
|
+
popOrigin = "rail";
|
|
737
|
+
syncPop();
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const provider = event.target?.closest?.("[data-dsh-quota-watch-pop-provider]");
|
|
741
|
+
if (provider) {
|
|
742
|
+
openKey = provider.dataset.dshQuotaWatchPopProvider;
|
|
743
|
+
popOrigin = "rail";
|
|
744
|
+
syncPop();
|
|
745
|
+
}
|
|
746
|
+
};
|
|
567
747
|
const onBodyClick = (event) => {
|
|
568
748
|
if (event.target?.closest?.('[data-action="refresh"]')) {
|
|
569
749
|
void poll(true);
|
|
@@ -585,6 +765,7 @@ function mountQuotaCard({
|
|
|
585
765
|
const target = event.target;
|
|
586
766
|
if (pop.contains(target) || target?.closest?.("[data-dsh-quota-watch-row]") || target?.closest?.(CARD_SELECTOR)) return;
|
|
587
767
|
openKey = void 0;
|
|
768
|
+
popOrigin = "row";
|
|
588
769
|
renderAll();
|
|
589
770
|
};
|
|
590
771
|
const onDocScroll = () => {
|
|
@@ -596,11 +777,14 @@ function mountQuotaCard({
|
|
|
596
777
|
const onDocKeydown = (event) => {
|
|
597
778
|
if (event.key === "Escape" && openKey !== void 0) {
|
|
598
779
|
openKey = void 0;
|
|
780
|
+
popOrigin = "row";
|
|
599
781
|
renderAll();
|
|
600
782
|
}
|
|
601
783
|
};
|
|
602
784
|
body.addEventListener("click", onBodyClick);
|
|
603
785
|
body.addEventListener("keydown", onBodyKeydown);
|
|
786
|
+
railTrigger.addEventListener("click", onRailClick);
|
|
787
|
+
pop.addEventListener("click", onPopClick);
|
|
604
788
|
doc.addEventListener("visibilitychange", onVisibilityChange);
|
|
605
789
|
doc.addEventListener("pointerdown", onDocPointerDown, true);
|
|
606
790
|
doc.addEventListener("scroll", onDocScroll, true);
|
|
@@ -618,6 +802,8 @@ function mountQuotaCard({
|
|
|
618
802
|
win.removeEventListener("resize", onWinResize);
|
|
619
803
|
body.removeEventListener("click", onBodyClick);
|
|
620
804
|
body.removeEventListener("keydown", onBodyKeydown);
|
|
805
|
+
railTrigger.removeEventListener("click", onRailClick);
|
|
806
|
+
pop.removeEventListener("click", onPopClick);
|
|
621
807
|
container.remove();
|
|
622
808
|
pop.remove();
|
|
623
809
|
};
|
package/package.json
CHANGED
package/src/client.mjs
CHANGED
|
@@ -10,6 +10,9 @@ const FETCH_TIMEOUT_MS = 15_000
|
|
|
10
10
|
const COPY = {
|
|
11
11
|
zh: {
|
|
12
12
|
title: '套餐监控',
|
|
13
|
+
openDetails: '查看详情',
|
|
14
|
+
backToOverview: '返回额度概览',
|
|
15
|
+
providerNames: { glm: 'GLM', copilot: 'Copilot' },
|
|
13
16
|
refresh: '刷新',
|
|
14
17
|
loading: '读取中',
|
|
15
18
|
failed: '查询失败',
|
|
@@ -24,7 +27,7 @@ const COPY = {
|
|
|
24
27
|
todayCalls: '调用次数',
|
|
25
28
|
weekUnlimited: '每周额度',
|
|
26
29
|
unlimited: '♾️',
|
|
27
|
-
unlimitedNote: '
|
|
30
|
+
unlimitedNote: '( 无限 )',
|
|
28
31
|
windows: {
|
|
29
32
|
'5h': '5 小时额度',
|
|
30
33
|
week: '每周额度',
|
|
@@ -34,6 +37,9 @@ const COPY = {
|
|
|
34
37
|
},
|
|
35
38
|
en: {
|
|
36
39
|
title: 'Quota Watch',
|
|
40
|
+
openDetails: 'View details',
|
|
41
|
+
backToOverview: 'Back to quota overview',
|
|
42
|
+
providerNames: { glm: 'GLM', copilot: 'Copilot' },
|
|
37
43
|
refresh: 'Refresh',
|
|
38
44
|
loading: 'Loading',
|
|
39
45
|
failed: 'Query failed',
|
|
@@ -48,7 +54,7 @@ const COPY = {
|
|
|
48
54
|
todayCalls: 'Calls',
|
|
49
55
|
weekUnlimited: 'Weekly quota',
|
|
50
56
|
unlimited: '♾️',
|
|
51
|
-
unlimitedNote: ' (unlimited)',
|
|
57
|
+
unlimitedNote: ' ( unlimited )',
|
|
52
58
|
windows: {
|
|
53
59
|
'5h': '5-hour quota',
|
|
54
60
|
week: 'Weekly quota',
|
|
@@ -81,10 +87,26 @@ const STYLE_TEXT = `
|
|
|
81
87
|
.dqw-chev { font-size: 9px; line-height: 14px; opacity: .6; flex: none; width: 10px; text-align: center; }
|
|
82
88
|
.dqw-stale-mark { flex: none; font-size: 9px; line-height: 14px; color: var(--dsw-alias-label-warning, #b46900); cursor: help; }
|
|
83
89
|
.dqw-errtext { font-size: 10px; line-height: 14px; opacity: .7; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
90
|
+
.dqw-rail-trigger { display: none; flex: none; align-items: center; justify-content: center; width: 36px; height: 36px; margin: 0; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--dsw-alias-label-primary, inherit); cursor: pointer; transition: background-color .12s, color .12s; }
|
|
91
|
+
.dqw-rail-trigger svg { display: block; width: 16px; height: 16px; }
|
|
92
|
+
.dqw-rail-trigger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); color: var(--dsw-alias-label-primary, inherit); }
|
|
93
|
+
.dqw-rail-trigger:active:not(:disabled), .dqw-rail-trigger[aria-expanded="true"] { background: var(--dsw-alias-interactive-bg-active, rgba(128,128,128,.18)); color: var(--dsw-alias-label-primary, inherit); }
|
|
94
|
+
.dqw-rail-trigger:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #5b8def); outline-offset: 2px; }
|
|
95
|
+
[data-dsh-frame][data-sidebar-collapsed] [data-dsh-quota-watch-card] { display: flex; align-items: center; justify-content: center; width: 36px; margin: 0 0 12px; }
|
|
96
|
+
[data-dsh-frame][data-sidebar-collapsed] [data-dsh-quota-watch-card] > .dqw-card { display: none; }
|
|
97
|
+
[data-dsh-frame][data-sidebar-collapsed] [data-dsh-quota-watch-card] > .dqw-rail-trigger { display: inline-flex; }
|
|
84
98
|
.dqw-pop { position: fixed; z-index: 2147483000; min-width: 166px; max-width: 320px; padding: 10px 12px; border: 1px solid var(--dsw-alias-border-secondary, rgba(128,128,128,.35)); border-radius: 10px; background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f)); background: color-mix(in srgb, var(--dsw-alias-bg-base, #1f1f1f) 86%, transparent); -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3); box-shadow: 0 8px 24px rgba(0,0,0,.25); color: var(--dsw-alias-label-primary, inherit); display: flex; flex-direction: column; gap: 8px; font: inherit; }
|
|
85
99
|
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .dqw-pop { background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f)); } }
|
|
86
100
|
.dqw-pop[hidden] { display: none; }
|
|
87
101
|
.dqw-pop *, .dqw-pop *::before, .dqw-pop *::after { box-sizing: border-box; }
|
|
102
|
+
.dqw-pop-title { margin: 0; font-size: 11px; font-weight: 600; line-height: 16px; }
|
|
103
|
+
.dqw-pop-back { align-self: flex-start; margin: 0; padding: 3px 5px; border: 0; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, inherit); font: inherit; font-size: 10.5px; line-height: 14px; cursor: pointer; }
|
|
104
|
+
.dqw-pop-back:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); color: var(--dsw-alias-label-primary, inherit); }
|
|
105
|
+
.dqw-overview-list { display: flex; flex-direction: column; gap: 2px; }
|
|
106
|
+
.dqw-overview-row { display: flex; align-items: center; width: 100%; gap: 6px; margin: 0; padding: 5px 4px; border: 0; border-radius: 8px; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
|
|
107
|
+
.dqw-overview-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); }
|
|
108
|
+
.dqw-overview-row:focus-visible { outline: 1px solid var(--dsw-alias-brand-primary, #5b8def); outline-offset: -1px; }
|
|
109
|
+
.dqw-overview-error { display: flex; align-items: center; gap: 6px; padding: 5px 4px; }
|
|
88
110
|
.dqw-bignums { display: flex; gap: 6px; }
|
|
89
111
|
.dqw-big { flex: 1; border: 1px solid var(--dsw-alias-border-secondary, rgba(128,128,128,.25)); border-radius: 8px; padding: 5px 8px; }
|
|
90
112
|
.dqw-big .dqw-big-value { font-size: 15px; font-weight: 650; line-height: 19px; font-variant-numeric: tabular-nums; }
|
|
@@ -147,6 +169,33 @@ function text(doc, tag, className, value) {
|
|
|
147
169
|
return node
|
|
148
170
|
}
|
|
149
171
|
|
|
172
|
+
function quotaIcon(doc) {
|
|
173
|
+
const namespace = 'http://www.w3.org/2000/svg'
|
|
174
|
+
const svg = doc.createElementNS(namespace, 'svg')
|
|
175
|
+
svg.setAttribute('viewBox', '0 0 24 24')
|
|
176
|
+
svg.setAttribute('width', '16')
|
|
177
|
+
svg.setAttribute('height', '16')
|
|
178
|
+
svg.setAttribute('fill', 'none')
|
|
179
|
+
svg.setAttribute('stroke', 'currentColor')
|
|
180
|
+
svg.setAttribute('stroke-width', '1.7')
|
|
181
|
+
svg.setAttribute('stroke-linecap', 'round')
|
|
182
|
+
svg.setAttribute('stroke-linejoin', 'round')
|
|
183
|
+
svg.setAttribute('aria-hidden', 'true')
|
|
184
|
+
svg.setAttribute('focusable', 'false')
|
|
185
|
+
const arc = doc.createElementNS(namespace, 'path')
|
|
186
|
+
arc.setAttribute('d', 'M4 17a8 8 0 0 1 16 0')
|
|
187
|
+
const needle = doc.createElementNS(namespace, 'path')
|
|
188
|
+
needle.setAttribute('d', 'm12 17 3.2-5.2')
|
|
189
|
+
const hub = doc.createElementNS(namespace, 'circle')
|
|
190
|
+
hub.setAttribute('cx', '12')
|
|
191
|
+
hub.setAttribute('cy', '17')
|
|
192
|
+
hub.setAttribute('r', '1')
|
|
193
|
+
hub.setAttribute('fill', 'currentColor')
|
|
194
|
+
hub.setAttribute('stroke', 'none')
|
|
195
|
+
svg.append(arc, needle, hub)
|
|
196
|
+
return svg
|
|
197
|
+
}
|
|
198
|
+
|
|
150
199
|
/** Aligned label/value line used by the simplified Copilot detail. */
|
|
151
200
|
function kvRow(doc, label, value) {
|
|
152
201
|
const row = doc.createElement('div')
|
|
@@ -228,6 +277,15 @@ export function mountQuotaCard({
|
|
|
228
277
|
const card = doc.createElement('section')
|
|
229
278
|
card.className = 'dqw-card'
|
|
230
279
|
card.setAttribute('aria-label', copy.title)
|
|
280
|
+
const railTrigger = doc.createElement('button')
|
|
281
|
+
railTrigger.type = 'button'
|
|
282
|
+
railTrigger.className = 'dqw-rail-trigger'
|
|
283
|
+
railTrigger.title = copy.title
|
|
284
|
+
railTrigger.setAttribute('aria-label', copy.title)
|
|
285
|
+
railTrigger.setAttribute('aria-haspopup', 'dialog')
|
|
286
|
+
railTrigger.setAttribute('aria-controls', 'dqw-quota-watch-pop')
|
|
287
|
+
railTrigger.setAttribute('aria-expanded', 'false')
|
|
288
|
+
railTrigger.append(quotaIcon(doc))
|
|
231
289
|
const body = doc.createElement('div')
|
|
232
290
|
body.className = 'dqw-body'
|
|
233
291
|
// Status line doubles as the transport-error surface; hidden once data renders.
|
|
@@ -235,10 +293,12 @@ export function mountQuotaCard({
|
|
|
235
293
|
lastUpdated.dataset.role = 'updated'
|
|
236
294
|
body.append(lastUpdated)
|
|
237
295
|
card.append(body)
|
|
238
|
-
container.append(style, card)
|
|
239
|
-
// Details open in a
|
|
296
|
+
container.append(style, card, railTrigger)
|
|
297
|
+
// Details and collapsed-rail summaries open in a viewport-level panel.
|
|
240
298
|
const pop = doc.createElement('div')
|
|
241
299
|
pop.className = 'dqw-pop'
|
|
300
|
+
pop.id = 'dqw-quota-watch-pop'
|
|
301
|
+
pop.setAttribute('role', 'dialog')
|
|
242
302
|
pop.dataset.dshQuotaWatchPop = ''
|
|
243
303
|
pop.hidden = true
|
|
244
304
|
doc.body.append(pop)
|
|
@@ -248,6 +308,7 @@ export function mountQuotaCard({
|
|
|
248
308
|
let disposed = false
|
|
249
309
|
let queuedPlace = false
|
|
250
310
|
let openKey
|
|
311
|
+
let popOrigin = 'row'
|
|
251
312
|
|
|
252
313
|
const fillClass = (percent) => (percent > 95 ? 'dqw-bar-fill danger' : percent > 80 ? 'dqw-bar-fill warn' : 'dqw-bar-fill')
|
|
253
314
|
|
|
@@ -299,8 +360,75 @@ export function mountQuotaCard({
|
|
|
299
360
|
return [row]
|
|
300
361
|
}
|
|
301
362
|
|
|
302
|
-
|
|
363
|
+
const renderOverviewProvider = (key, model) => {
|
|
364
|
+
if (model === null) return null
|
|
365
|
+
const label = text(doc, 'span', 'dqw-label', model.label)
|
|
366
|
+
if (model.kind === 'error') {
|
|
367
|
+
const row = doc.createElement('div')
|
|
368
|
+
row.className = 'dqw-overview-error'
|
|
369
|
+
row.append(label, text(doc, 'span', 'dqw-errtext', model.error ? `${copy.failed}: ${model.error}` : copy.failed))
|
|
370
|
+
return row
|
|
371
|
+
}
|
|
372
|
+
const row = doc.createElement('button')
|
|
373
|
+
row.type = 'button'
|
|
374
|
+
row.className = 'dqw-overview-row'
|
|
375
|
+
row.dataset.dshQuotaWatchPopProvider = key
|
|
376
|
+
row.setAttribute('aria-expanded', 'false')
|
|
377
|
+
row.setAttribute('aria-label', `${model.label} · ${typeof model.percent === 'number' ? formatPercent(model.percent, locale) : '—'} · ${copy.openDetails}`)
|
|
378
|
+
row.append(label)
|
|
379
|
+
if (typeof model.percent === 'number' && Number.isFinite(model.percent)) {
|
|
380
|
+
const bar = doc.createElement('span')
|
|
381
|
+
bar.className = 'dqw-bar'
|
|
382
|
+
bar.setAttribute('role', 'progressbar')
|
|
383
|
+
bar.setAttribute('aria-valuemin', '0')
|
|
384
|
+
bar.setAttribute('aria-valuemax', '100')
|
|
385
|
+
bar.setAttribute('aria-valuenow', String(Math.max(0, Math.min(100, model.percent))))
|
|
386
|
+
const fill = text(doc, 'span', fillClass(model.percent))
|
|
387
|
+
fill.style.width = `${Math.max(0, Math.min(100, model.percent))}%`
|
|
388
|
+
bar.append(fill)
|
|
389
|
+
row.append(bar)
|
|
390
|
+
}
|
|
391
|
+
if (model.stale) {
|
|
392
|
+
const mark = text(doc, 'span', 'dqw-stale-mark', '⚠')
|
|
393
|
+
mark.dataset.dshQuotaWatchStale = ''
|
|
394
|
+
mark.title = model.error ?? copy.stale
|
|
395
|
+
row.append(mark)
|
|
396
|
+
}
|
|
397
|
+
row.append(text(doc, 'span', 'dqw-pct', typeof model.percent === 'number' ? formatPercent(model.percent, locale) : '—'))
|
|
398
|
+
row.append(text(doc, 'span', 'dqw-chev', '▸'))
|
|
399
|
+
return row
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const renderOverview = () => {
|
|
403
|
+
pop.replaceChildren(text(doc, 'p', 'dqw-pop-title', copy.title))
|
|
404
|
+
const providers = snapshot?.providers ?? []
|
|
405
|
+
const glm = providers.find((provider) => provider?.key === 'glm')
|
|
406
|
+
const copilot = providers.find((provider) => provider?.key === 'copilot')
|
|
407
|
+
const rows = [
|
|
408
|
+
renderOverviewProvider('glm', glmRowModel(glm)),
|
|
409
|
+
renderOverviewProvider('copilot', copilotRowModel(copilot)),
|
|
410
|
+
].filter(Boolean)
|
|
411
|
+
if (rows.length === 0) {
|
|
412
|
+
pop.append(text(doc, 'p', 'dqw-muted', lastUpdated.textContent || copy.loading))
|
|
413
|
+
return
|
|
414
|
+
}
|
|
415
|
+
const list = doc.createElement('div')
|
|
416
|
+
list.className = 'dqw-overview-list'
|
|
417
|
+
list.append(...rows)
|
|
418
|
+
pop.append(list)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function renderDetail(detail, key, provider, fromRail = false) {
|
|
303
422
|
detail.replaceChildren()
|
|
423
|
+
if (fromRail) {
|
|
424
|
+
const back = doc.createElement('button')
|
|
425
|
+
back.type = 'button'
|
|
426
|
+
back.className = 'dqw-pop-back'
|
|
427
|
+
back.dataset.action = 'back-to-overview'
|
|
428
|
+
back.setAttribute('aria-label', copy.backToOverview)
|
|
429
|
+
back.textContent = copy.backToOverview
|
|
430
|
+
detail.append(back, text(doc, 'p', 'dqw-pop-title', copy.providerNames[key]))
|
|
431
|
+
}
|
|
304
432
|
if (provider?.status === 'stale') {
|
|
305
433
|
detail.append(text(doc, 'p', 'dqw-error', `${copy.stale}${provider.error ? `: ${provider.error}` : ''}`))
|
|
306
434
|
}
|
|
@@ -360,7 +488,7 @@ export function mountQuotaCard({
|
|
|
360
488
|
wins.append(text(doc, 'span', 'dqw-win-mid', '·'))
|
|
361
489
|
wins.append(text(doc, 'span', 'dqw-win-used', copy.windowUsed))
|
|
362
490
|
wins.append(text(doc, 'span', 'dqw-win-val', percent !== undefined ? formatPercent(percent, locale) : '—'))
|
|
363
|
-
wins.append(text(doc, 'span', 'dqw-kv-sub', reset ?
|
|
491
|
+
wins.append(text(doc, 'span', 'dqw-kv-sub', reset ? `( ${copy.reset}: ${reset} )` : ''))
|
|
364
492
|
}
|
|
365
493
|
detail.append(wins)
|
|
366
494
|
if (Array.isArray(usage?.models) && usage.models.length > 0) {
|
|
@@ -401,12 +529,14 @@ export function mountQuotaCard({
|
|
|
401
529
|
}
|
|
402
530
|
}
|
|
403
531
|
|
|
404
|
-
/**
|
|
532
|
+
/** Anchor the popover to the rail icon or the expanded provider row. */
|
|
405
533
|
const positionPop = () => {
|
|
406
534
|
if (pop.hidden || openKey === undefined) return
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
535
|
+
const anchor = popOrigin === 'rail'
|
|
536
|
+
? railTrigger
|
|
537
|
+
: body.querySelector(`[data-dsh-quota-watch-row="${openKey}"]`)
|
|
538
|
+
if (!anchor) return
|
|
539
|
+
const rect = anchor.getBoundingClientRect()
|
|
410
540
|
const viewWidth = win.innerWidth ?? 1024
|
|
411
541
|
const viewHeight = win.innerHeight ?? 768
|
|
412
542
|
const width = pop.offsetWidth || 240
|
|
@@ -421,24 +551,35 @@ export function mountQuotaCard({
|
|
|
421
551
|
}
|
|
422
552
|
|
|
423
553
|
const syncPop = () => {
|
|
554
|
+
const fromRail = popOrigin === 'rail' && openKey !== undefined && !disposed
|
|
555
|
+
railTrigger.setAttribute('aria-expanded', String(fromRail))
|
|
424
556
|
if (openKey === undefined || disposed) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
557
|
+
pop.hidden = true
|
|
558
|
+
pop.replaceChildren()
|
|
559
|
+
delete pop.dataset.dshQuotaWatchDetail
|
|
560
|
+
delete pop.dataset.dshQuotaWatchOverview
|
|
561
|
+
return
|
|
562
|
+
}
|
|
563
|
+
if (openKey === 'overview') {
|
|
564
|
+
renderOverview()
|
|
565
|
+
delete pop.dataset.dshQuotaWatchDetail
|
|
566
|
+
pop.dataset.dshQuotaWatchOverview = ''
|
|
567
|
+
pop.setAttribute('aria-label', copy.title)
|
|
568
|
+
pop.hidden = false
|
|
569
|
+
positionPop()
|
|
430
570
|
return
|
|
431
571
|
}
|
|
432
572
|
const provider = (snapshot?.providers ?? []).find((item) => item?.key === openKey)
|
|
433
573
|
const row = body.querySelector(`[data-dsh-quota-watch-row="${openKey}"]`)
|
|
434
|
-
if (!provider || !row || !row.hasAttribute('data-expandable')) {
|
|
574
|
+
if (!provider || (!fromRail && (!row || !row.hasAttribute('data-expandable')))) {
|
|
435
575
|
openKey = undefined
|
|
436
576
|
syncPop()
|
|
437
577
|
return
|
|
438
578
|
}
|
|
439
|
-
renderDetail(pop, openKey, provider)
|
|
579
|
+
renderDetail(pop, openKey, provider, fromRail)
|
|
440
580
|
pop.dataset.dshQuotaWatchDetail = openKey
|
|
441
|
-
pop.
|
|
581
|
+
delete pop.dataset.dshQuotaWatchOverview
|
|
582
|
+
pop.setAttribute('aria-label', fromRail ? `${copy.providerNames[openKey]} · ${copy.title}` : copy.providerNames[openKey])
|
|
442
583
|
pop.hidden = false
|
|
443
584
|
positionPop()
|
|
444
585
|
}
|
|
@@ -506,8 +647,27 @@ export function mountQuotaCard({
|
|
|
506
647
|
})
|
|
507
648
|
}
|
|
508
649
|
|
|
509
|
-
|
|
510
|
-
|
|
650
|
+
let sidebarCollapsed = Boolean(doc.querySelector('[data-dsh-frame][data-sidebar-collapsed]'))
|
|
651
|
+
const syncSidebarMode = () => {
|
|
652
|
+
const nextCollapsed = Boolean(doc.querySelector('[data-dsh-frame][data-sidebar-collapsed]'))
|
|
653
|
+
if (nextCollapsed === sidebarCollapsed) return
|
|
654
|
+
sidebarCollapsed = nextCollapsed
|
|
655
|
+
if (openKey !== undefined) {
|
|
656
|
+
openKey = undefined
|
|
657
|
+
popOrigin = 'row'
|
|
658
|
+
syncPop()
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
const observer = new win.MutationObserver(() => {
|
|
662
|
+
syncSidebarMode()
|
|
663
|
+
schedulePlace()
|
|
664
|
+
})
|
|
665
|
+
observer.observe(doc.body, {
|
|
666
|
+
childList: true,
|
|
667
|
+
subtree: true,
|
|
668
|
+
attributes: true,
|
|
669
|
+
attributeFilter: ['data-sidebar-collapsed'],
|
|
670
|
+
})
|
|
511
671
|
place()
|
|
512
672
|
|
|
513
673
|
const startPolling = () => {
|
|
@@ -524,9 +684,34 @@ export function mountQuotaCard({
|
|
|
524
684
|
else startPolling()
|
|
525
685
|
}
|
|
526
686
|
const toggleRow = (key) => {
|
|
687
|
+
popOrigin = 'row'
|
|
527
688
|
openKey = openKey === key ? undefined : key
|
|
528
689
|
renderAll()
|
|
529
690
|
}
|
|
691
|
+
const onRailClick = () => {
|
|
692
|
+
if (popOrigin === 'rail' && openKey !== undefined) {
|
|
693
|
+
openKey = undefined
|
|
694
|
+
popOrigin = 'row'
|
|
695
|
+
} else {
|
|
696
|
+
openKey = 'overview'
|
|
697
|
+
popOrigin = 'rail'
|
|
698
|
+
}
|
|
699
|
+
syncPop()
|
|
700
|
+
}
|
|
701
|
+
const onPopClick = (event) => {
|
|
702
|
+
if (event.target?.closest?.('[data-action="back-to-overview"]')) {
|
|
703
|
+
openKey = 'overview'
|
|
704
|
+
popOrigin = 'rail'
|
|
705
|
+
syncPop()
|
|
706
|
+
return
|
|
707
|
+
}
|
|
708
|
+
const provider = event.target?.closest?.('[data-dsh-quota-watch-pop-provider]')
|
|
709
|
+
if (provider) {
|
|
710
|
+
openKey = provider.dataset.dshQuotaWatchPopProvider
|
|
711
|
+
popOrigin = 'rail'
|
|
712
|
+
syncPop()
|
|
713
|
+
}
|
|
714
|
+
}
|
|
530
715
|
const onBodyClick = (event) => {
|
|
531
716
|
if (event.target?.closest?.('[data-action="refresh"]')) {
|
|
532
717
|
void poll(true)
|
|
@@ -548,6 +733,7 @@ export function mountQuotaCard({
|
|
|
548
733
|
const target = event.target
|
|
549
734
|
if (pop.contains(target) || target?.closest?.('[data-dsh-quota-watch-row]') || target?.closest?.(CARD_SELECTOR)) return
|
|
550
735
|
openKey = undefined
|
|
736
|
+
popOrigin = 'row'
|
|
551
737
|
renderAll()
|
|
552
738
|
}
|
|
553
739
|
const onDocScroll = () => { positionPop() }
|
|
@@ -555,11 +741,14 @@ export function mountQuotaCard({
|
|
|
555
741
|
const onDocKeydown = (event) => {
|
|
556
742
|
if (event.key === 'Escape' && openKey !== undefined) {
|
|
557
743
|
openKey = undefined
|
|
744
|
+
popOrigin = 'row'
|
|
558
745
|
renderAll()
|
|
559
746
|
}
|
|
560
747
|
}
|
|
561
748
|
body.addEventListener('click', onBodyClick)
|
|
562
749
|
body.addEventListener('keydown', onBodyKeydown)
|
|
750
|
+
railTrigger.addEventListener('click', onRailClick)
|
|
751
|
+
pop.addEventListener('click', onPopClick)
|
|
563
752
|
doc.addEventListener('visibilitychange', onVisibilityChange)
|
|
564
753
|
doc.addEventListener('pointerdown', onDocPointerDown, true)
|
|
565
754
|
doc.addEventListener('scroll', onDocScroll, true)
|
|
@@ -578,6 +767,8 @@ export function mountQuotaCard({
|
|
|
578
767
|
win.removeEventListener('resize', onWinResize)
|
|
579
768
|
body.removeEventListener('click', onBodyClick)
|
|
580
769
|
body.removeEventListener('keydown', onBodyKeydown)
|
|
770
|
+
railTrigger.removeEventListener('click', onRailClick)
|
|
771
|
+
pop.removeEventListener('click', onPopClick)
|
|
581
772
|
container.remove()
|
|
582
773
|
pop.remove()
|
|
583
774
|
}
|