diff --git a/internal/gui/assets/app.css b/internal/gui/assets/app.css index 1697a549..538719f6 100644 --- a/internal/gui/assets/app.css +++ b/internal/gui/assets/app.css @@ -432,6 +432,7 @@ body.window .field.solo { grid-column: auto; } .subscription-card.several .quota-resets + .subscription-account { margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--line); } .reset-ask .reset-idle { color: var(--amber); } .quota-balance { display: flex; align-items: baseline; gap: 6px; font-size: 10.5px; color: var(--muted); } +.quota-windows + .quota-balance { margin-top: 9px; } .quota-balance b { margin-left: auto; font-size: 15px; color: var(--fg); font-weight: 600; font-variant-numeric: tabular-nums; } .skeleton { display: block; border-radius: 5px; background: linear-gradient(90deg, var(--pill) 25%, var(--pill-hover) 45%, var(--pill) 65%); background-size: 240% 100%; animation: skeleton-shift 1.4s ease-in-out infinite; } @keyframes skeleton-shift { from { background-position: 100% 0; } to { background-position: -100% 0; } } diff --git a/internal/gui/assets/app.js b/internal/gui/assets/app.js index a4a95586..2f500b27 100644 --- a/internal/gui/assets/app.js +++ b/internal/gui/assets/app.js @@ -4199,7 +4199,7 @@ function renderQuotas() { // balance is only ever what is left) $("#quotaHead").hidden = !!quotas && !quotas.length; const mode = $("#quotaMode"); - mode.hidden = !quotas?.some((q) => !q.balance && !q.error && q.windows?.length); + mode.hidden = !quotas?.some((q) => !q.error && q.windows?.length); if (!mode.hidden) { mode.replaceChildren(); for (const [left, name] of [[false, "Used"], [true, "Left"]]) { @@ -4243,6 +4243,8 @@ function renderQuotas() { card.append(who); } card.append(quotaWindows(sub)); + // what is left besides the windows, under them + if (sub.balance && sub.windows?.length && !sub.error) card.append(balanceRow(sub, "What is left on the account besides its windows")); if (sub.resets?.count) { const r = el("div", "quota-resets"); r.append(resetsWords(sub.resets)); @@ -4344,7 +4346,9 @@ function renderPanelQuota() { const groups = new Map(); const bals = []; for (const q of subs) { - if (q.balance) { bals.push(q); continue; } + // a balance with windows (Command Code's credits beside its 5-hour and + // weekly windows) is the windows' card; a balance alone, a figure + if (q.balance && !q.windows?.length) { bals.push(q); continue; } if (!groups.has(q.name)) groups.set(q.name, []); groups.get(q.name).push(q); } @@ -4359,7 +4363,7 @@ function renderPanelQuota() { n.title = t("{n} accounts", { n: qs.length }); head.append(n); } else { - const note = [qs[0].plan, qs[0].until ? planTerm(qs[0]) : ""].filter(Boolean).join(" · "); + const note = [qs[0].plan, qs[0].until ? planTerm(qs[0]) : "", qs[0].balance].filter(Boolean).join(" · "); head.append(el("span", "pq-gnote" + (qs[0].renew === "off" ? " ends" : ""), note)); } g.append(head); @@ -4403,7 +4407,7 @@ function shortWindow(name) { function panelQuotaCard(q) { const card = el("div", "pq-card"); card.append(el("span", "pq-user", q.user || q.name)); - card.title = [q.name, q.user, q.plan, q.until ? planTerm(q) : ""].filter(Boolean).join(" · "); + card.title = [q.name, q.user, q.plan, q.until ? planTerm(q) : "", q.balance && t("Balance") + " " + q.balance].filter(Boolean).join(" · "); if (q.error) { card.classList.add("err"); card.append(el("span", "pq-sub err", quotaError(q.error))); @@ -4556,14 +4560,17 @@ const quotaFit = new ResizeObserver((es) => { } }); +// balanceRow: what is left on an account, as a figure. +function balanceRow(sub, why) { + const b = el("div", "quota-balance"); + b.title = t(why); + b.append(el("span", "", t("Balance")), el("b", "", sub.balance)); + return b; +} + // quotaWindows: one account's allowance as meters, or why there are none. function quotaWindows(sub) { - if (sub.balance) { - const b = el("div", "quota-balance"); - b.title = t("What is left on the account: the vendor tells only this, so Used / Left leaves it as it is"); - b.append(el("span", "", t("Balance")), el("b", "", sub.balance)); - return b; - } + if (sub.balance && !sub.windows?.length) return balanceRow(sub, "What is left on the account: the vendor tells only this, so Used / Left leaves it as it is"); if (sub.error) { const e = el("div", "subscription-error", quotaError(sub.error)); e.title = sub.error; diff --git a/internal/gui/assets/i18n.js b/internal/gui/assets/i18n.js index 7a98667d..af6eef7b 100644 --- a/internal/gui/assets/i18n.js +++ b/internal/gui/assets/i18n.js @@ -420,6 +420,7 @@ const I18N = { "that reset was already used": "这次重置已经用过了", "Allowances": "额度", "What is left on the account: the vendor tells only this, so Used / Left leaves it as it is": "账户里剩下的金额:供应商只提供这个数,所以「已用 / 剩余」不会改变它", + "What is left on the account besides its windows": "账户里除用量窗口外剩下的额度", "Headers": "请求头", "Extra HTTP headers sent to the vendor, applied after auth. For gateways that need a private scheme.": "发送给供应商的额外 HTTP 请求头,在鉴权之后应用。适用于需要私有鉴权方案的网关。", "Header-Name": "请求头名称",