2 Commits
Author SHA1 Message Date
yuez 9d1884cf20 feat(quota): All tab Command Code card shows Plan + 5h/weekly/monthly windows with reset times (v0.4.4) 2026-09-20 15:25:29 +08:00
yuez 447ce65c9b feat(quota): All tab first in tab bar; remove header usage-alert badge (v0.4.3)
- Tab order: All | Command Code | OpenCode Go (All remains the default)
- Drop the global status badge in the header action row and its
  updateGlobalBadge aggregation; per-card chips and All-tab provider
  badges are unaffected
2026-09-20 14:52:24 +08:00
3 changed files with 41 additions and 38 deletions
+2 -2
View File
@@ -74,8 +74,8 @@ func TestHandleManagement_QuotaResource(t *testing.T) {
if !strings.Contains(bodyStr, "用量配额") { if !strings.Contains(bodyStr, "用量配额") {
t.Errorf("Body does not contain expected menu text 用量配额") t.Errorf("Body does not contain expected menu text 用量配额")
} }
if !strings.Contains(bodyStr, "v0.4.2") { if !strings.Contains(bodyStr, "v0.4.4") {
t.Errorf("Body does not contain version badge v0.4.2") t.Errorf("Body does not contain version badge v0.4.4")
} }
} }
} }
+1 -1
View File
@@ -13,7 +13,7 @@ import (
const ( const (
PluginID = "commandcode" PluginID = "commandcode"
PluginName = "commandcode" PluginName = "commandcode"
PluginVersion = "0.4.2" PluginVersion = "0.4.4"
PluginAuthor = "zgs225" PluginAuthor = "zgs225"
PluginRepo = "https://github.com/zgs225/cliproxy-plugin-commandcode" PluginRepo = "https://github.com/zgs225/cliproxy-plugin-commandcode"
PluginLogo = "https://raw.githubusercontent.com/zgs225/cliproxy-plugin-commandcode/main/assets/logo.svg" PluginLogo = "https://raw.githubusercontent.com/zgs225/cliproxy-plugin-commandcode/main/assets/logo.svg"
+38 -35
View File
@@ -918,17 +918,12 @@ const QuotaPageHTML = `<!DOCTYPE html>
<div> <div>
<div class="brand-title"> <div class="brand-title">
用量配额 用量配额
<span class="version-tag">v0.4.2</span> <span class="version-tag">v0.4.4</span>
</div> </div>
</div> </div>
</div> </div>
<div class="action-group"> <div class="action-group">
<div id="statusBadge" class="status-badge">
<span class="status-dot"></span>
<span id="statusText">正在检查...</span>
</div>
<button id="btnSettings" class="btn" title="配置选项"> <button id="btnSettings" class="btn" title="配置选项">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"></circle> <circle cx="12" cy="12" r="3"></circle>
@@ -949,9 +944,9 @@ const QuotaPageHTML = `<!DOCTYPE html>
<!-- Tab Bar --> <!-- Tab Bar -->
<div id="tabBar" class="tab-bar"> <div id="tabBar" class="tab-bar">
<button type="button" class="tab-btn active" data-tab="all">All</button>
<button type="button" class="tab-btn" data-tab="commandcode">Command Code</button> <button type="button" class="tab-btn" data-tab="commandcode">Command Code</button>
<button type="button" class="tab-btn" data-tab="opencode">OpenCode Go</button> <button type="button" class="tab-btn" data-tab="opencode">OpenCode Go</button>
<button type="button" class="tab-btn active" data-tab="all">All</button>
</div> </div>
<!-- Alert Message --> <!-- Alert Message -->
@@ -1191,8 +1186,6 @@ const QuotaPageHTML = `<!DOCTYPE html>
const inputOpenCodeKeys = document.getElementById("inputOpenCodeKeys"); const inputOpenCodeKeys = document.getElementById("inputOpenCodeKeys");
const alertBox = document.getElementById("alertBox"); const alertBox = document.getElementById("alertBox");
const alertMsg = document.getElementById("alertMsg"); const alertMsg = document.getElementById("alertMsg");
const statusBadge = document.getElementById("statusBadge");
const statusText = document.getElementById("statusText");
const planBadge = document.getElementById("planBadge"); const planBadge = document.getElementById("planBadge");
// Tab sections and per-provider error cards // Tab sections and per-provider error cards
@@ -1345,26 +1338,9 @@ const QuotaPageHTML = `<!DOCTYPE html>
return level === "error" ? "exceeded" : level === "unknown" ? "" : level; return level === "error" ? "exceeded" : level === "unknown" ? "" : level;
} }
function updateGlobalBadge() {
let p;
if (activeTab === "commandcode") {
p = providerState.commandcode;
} else if (activeTab === "opencode") {
p = providerState.opencode;
} else {
const a = providerState.commandcode;
const b = providerState.opencode;
p = LEVEL_RANK[a.level] >= LEVEL_RANK[b.level] ? a : b;
}
const visual = badgeVisualClass(p.level);
statusBadge.className = visual ? "status-badge " + visual : "status-badge";
statusText.textContent = BADGE_TEXT[p.level] || "正在检查...";
}
function setProviderStatus(provider, level) { function setProviderStatus(provider, level) {
providerState[provider].level = level; providerState[provider].level = level;
providerState[provider].err = null; providerState[provider].err = null;
updateGlobalBadge();
} }
function summaryRow(label, value) { function summaryRow(label, value) {
@@ -1390,7 +1366,6 @@ const QuotaPageHTML = `<!DOCTYPE html>
ocErrorMsg.textContent = msg; ocErrorMsg.textContent = msg;
ocErrorCard.classList.add("show"); ocErrorCard.classList.add("show");
} }
updateGlobalBadge();
} }
// Multi-window countdown elements carry their reset info in data // Multi-window countdown elements carry their reset info in data
@@ -1500,7 +1475,7 @@ const QuotaPageHTML = `<!DOCTYPE html>
weeklyTargetTime = null; weeklyTargetTime = null;
} }
// Per-provider status; the header badge is aggregated in updateGlobalBadge() // Per-provider status; consumed by All tab provider badges
let ccLevel; let ccLevel;
if (fiveHour.exceeded || weekly.exceeded || monthly.exceeded) { if (fiveHour.exceeded || weekly.exceeded || monthly.exceeded) {
ccLevel = "exceeded"; ccLevel = "exceeded";
@@ -1650,12 +1625,43 @@ const QuotaPageHTML = `<!DOCTYPE html>
}); });
allBadgeCommandcode.className = "status-badge " + badgeVisualClass(cc.level); allBadgeCommandcode.className = "status-badge " + badgeVisualClass(cc.level);
allBadgeTextCommandcode.textContent = BADGE_TEXT[cc.level] || "-"; allBadgeTextCommandcode.textContent = BADGE_TEXT[cc.level] || "-";
// Plan + 三个限额窗口(百分比/进度条/重置时间),行结构与 OpenCode 卡片一致,
// 倒计时走 data-reset-at/data-reset-secs 属性,由 updateTimers() 统一刷新
const ccWins = [
{ name: "Rolling 5h", o: limits.five_hour || {} },
{ name: "Weekly", o: limits.weekly || {} },
{ name: "Monthly", o: limits.monthly || {} }
];
let ccRows = "";
ccWins.forEach(function (d) {
const w = d.o;
const pct = clampPercent(w.percentage);
const level = levelOf(pct, w.exceeded, "");
let resetAt = "";
let resetSecs = "";
if (w.reset_at) {
const t = new Date(w.reset_at);
if (!isNaN(t.getTime())) resetAt = t.toISOString();
}
if (!resetAt && w.reset_in_seconds > 0) {
resetSecs = String(w.reset_in_seconds);
}
const resetAttr = resetAt
? " data-reset-at=\"" + esc(resetAt) + "\""
: (resetSecs ? " data-reset-secs=\"" + esc(resetSecs) + "\"" : "");
const pctText = pct === null ? "-" : Math.round(pct) + "%";
const pctCls = level === "exceeded" ? " bad" : pct !== null && pct >= 80 ? " warn" : "";
const barCls = level === "exceeded" ? " danger" : level === "warning" ? " warning" : "";
ccRows += '<div class="oc-win-row">' +
'<span class="oc-win-name">' + d.name + '</span>' +
'<div class="progress-track oc-win-bar"><div class="progress-bar' + barCls + '" style="width:' + (pct === null ? 0 : pct) + '%"></div></div>' +
'<span class="oc-win-pct' + pctCls + '">' + pctText + '</span>' +
'<span class="oc-win-reset countdown-timer"' + resetAttr + '>-</span>' +
'</div>';
});
allBodyCommandcode.innerHTML = allBodyCommandcode.innerHTML =
summaryRow("Plan", esc(planName)) + summaryRow("Plan", esc(planName)) +
summaryRow("Total Credits", formatUSD(credits.total_credits)) + '<div class="oc-key-body">' + ccRows + '</div>';
summaryRow("Monthly Credits", formatUSD(credits.monthly_credits)) +
summaryRow("最差窗口", esc(worstName) + " " + (worstPct === null ? "-" : worstPct.toFixed(1) + "%")) +
miniBar(worstPct, worstLevel);
} else { } else {
allBadgeCommandcode.className = "status-badge"; allBadgeCommandcode.className = "status-badge";
allBadgeTextCommandcode.textContent = "尚未加载"; allBadgeTextCommandcode.textContent = "尚未加载";
@@ -1692,7 +1698,6 @@ const QuotaPageHTML = `<!DOCTYPE html>
document.getElementById("sectionCommandcode").classList.toggle("active", tab === "commandcode"); document.getElementById("sectionCommandcode").classList.toggle("active", tab === "commandcode");
document.getElementById("sectionOpencode").classList.toggle("active", tab === "opencode"); document.getElementById("sectionOpencode").classList.toggle("active", tab === "opencode");
document.getElementById("sectionAll").classList.toggle("active", tab === "all"); document.getElementById("sectionAll").classList.toggle("active", tab === "all");
updateGlobalBadge();
if (updateHash) { if (updateHash) {
if (tab === "all") { if (tab === "all") {
// All 为默认 tab:激活 All 时清掉 hash(刷新回到默认) // All 为默认 tab:激活 All 时清掉 hash(刷新回到默认)
@@ -1741,8 +1746,6 @@ const QuotaPageHTML = `<!DOCTYPE html>
if (res.status === 401 || res.status === 403) { if (res.status === 401 || res.status === 403) {
settingsDrawer.classList.add("open"); settingsDrawer.classList.add("open");
showAlert("需要 CLIProxyAPI 管理密钥 (401/403)。请在上方输入框填入 Management Key 并保存。", true); showAlert("需要 CLIProxyAPI 管理密钥 (401/403)。请在上方输入框填入 Management Key 并保存。", true);
statusBadge.className = "status-badge warning";
statusText.textContent = "未授权";
return; return;
} }