mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 07:29:42 +02:00
fix(usage): make plan-usage chip work end-to-end + session-status footer
End-to-end testing on the real install surfaced several issues the unit tests missed: - Injection gate excluded real sessions: gated on workingDir under CASES_DIR, but sessions run in linked cases / real repos. Drop the gate (match updateCaseModel, which writes settings.local.json unconditionally). - statusLine curl failed on HTTPS: prod is loopback HTTPS with a self-signed cert; `curl -s` returns 000. Use `curl -sk` (loopback only). applyStatusLineConfig now also updates an out-of-date ours-command so the fix propagates. - Footer hijacked by limits: the in-terminal statusline now shows CURRENT SESSION status — `Opus 4.8 (1M context) in:562,411 out:1,188 ctx:56%` — while the account-wide plan limits live only in the header chip. - Chip blank after reload: persist last-known to localStorage and restore on load (account-global, slow-moving; 12h freshness guard). - Readability + color: per-window green/yellow/red by usage (<60 / 60–84 / ≥85), bolder labels and values. - Drop the renderIndexHtml strip (client-side reveal only, response-viewer pattern) — fixes server-index-title test fragility to local settings. Footer fields flow through context_window.total_input_tokens/total_output_tokens (schema + parser). Tests updated; verified live (footer, chip, colors, reload persistence) on the real install. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+28
-10
@@ -581,6 +581,7 @@ class CodemanApp {
|
||||
const _kbSettings = this.loadAppSettingsFromStorage();
|
||||
if (_kbSettings.extendedKeyboardBar) KeyboardAccessoryBar.setMode('extended');
|
||||
this.applyHeaderVisibilitySettings();
|
||||
this.restorePlanUsageChip();
|
||||
this.applySkin();
|
||||
this.applyTabWrapSettings();
|
||||
this.applyMonitorVisibility();
|
||||
@@ -1818,6 +1819,24 @@ class CodemanApp {
|
||||
_onSessionStatusTelemetry(data) {
|
||||
this._latestPlanUsage = data;
|
||||
this.updatePlanUsageChip(data);
|
||||
// Persist last-known so the chip shows immediately on the next page load /
|
||||
// SSE reconnect, instead of staying blank until a session next renders.
|
||||
try {
|
||||
localStorage.setItem('codeman:planUsage', JSON.stringify({ t: Date.now(), data }));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Repopulate the chip from the last-known value on page load (account-global,
|
||||
// slow-moving; ignored if older than 12h). Live events refresh it.
|
||||
restorePlanUsageChip() {
|
||||
try {
|
||||
const raw = localStorage.getItem('codeman:planUsage');
|
||||
if (!raw) return;
|
||||
const saved = JSON.parse(raw);
|
||||
if (saved?.data && Date.now() - (saved.t || 0) < 12 * 3600 * 1000) {
|
||||
this.updatePlanUsageChip(saved.data);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
updatePlanUsageChip(data) {
|
||||
@@ -1827,19 +1846,18 @@ class CodemanApp {
|
||||
const five = pct(data.fiveHour);
|
||||
const seven = pct(data.sevenDay);
|
||||
if (five === null && seven === null) return;
|
||||
const parts = [];
|
||||
if (five !== null) parts.push(`5h ${five}%`);
|
||||
if (seven !== null) parts.push(`7d ${seven}%`);
|
||||
chip.textContent = parts.join(' · ');
|
||||
// Color hint as a window nears exhaustion.
|
||||
const max = Math.max(five ?? 0, seven ?? 0);
|
||||
chip.classList.toggle('header-plan-usage--warn', max >= 80 && max < 95);
|
||||
chip.classList.toggle('header-plan-usage--crit', max >= 95);
|
||||
// Per-window color by how much is used up: green < 60%, yellow 60–84%, red ≥ 85%.
|
||||
const colorClass = (p) => (p >= 85 ? 'pu-red' : p >= 60 ? 'pu-yellow' : 'pu-green');
|
||||
const seg = (label, p) =>
|
||||
p === null
|
||||
? ''
|
||||
: `<span class="pu-win"><span class="pu-label">${label}</span><span class="pu-val ${colorClass(p)}">${p}%</span></span>`;
|
||||
chip.innerHTML = [seg('5h', five), seg('7d', seven)].filter(Boolean).join('<span class="pu-sep">·</span>');
|
||||
const resetStr = (w) => (w && w.resetAt ? new Date(w.resetAt).toLocaleString() : '—');
|
||||
chip.title =
|
||||
`Claude plan usage\n` +
|
||||
`5-hour: ${five ?? '—'}% (resets ${resetStr(data.fiveHour)})\n` +
|
||||
`Weekly: ${seven ?? '—'}% (resets ${resetStr(data.sevenDay)})`;
|
||||
`5-hour limit: ${five ?? '—'}% used (resets ${resetStr(data.fiveHour)})\n` +
|
||||
`Weekly limit: ${seven ?? '—'}% used (resets ${resetStr(data.sevenDay)})`;
|
||||
}
|
||||
|
||||
// Scheduled runs
|
||||
|
||||
@@ -8341,13 +8341,37 @@ kbd {
|
||||
white-space: nowrap;
|
||||
cursor: default;
|
||||
}
|
||||
.header-plan-usage--warn {
|
||||
color: #e6b800;
|
||||
border-color: rgba(230, 184, 0, 0.4);
|
||||
/* Readable two-window layout: dim uppercase label + bold, color-coded value. */
|
||||
.header-plan-usage .pu-win {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 4px;
|
||||
}
|
||||
.header-plan-usage--crit {
|
||||
color: var(--danger, #ff5c5c);
|
||||
border-color: rgba(255, 92, 92, 0.5);
|
||||
.header-plan-usage .pu-label {
|
||||
font-size: 0.66rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
opacity: 0.9;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.header-plan-usage .pu-val {
|
||||
font-weight: 700;
|
||||
}
|
||||
.header-plan-usage .pu-sep {
|
||||
margin: 0 7px;
|
||||
color: var(--text-dim);
|
||||
opacity: 0.45;
|
||||
}
|
||||
/* Green/yellow/red by how much of the window is used up. */
|
||||
.header-plan-usage .pu-green {
|
||||
color: #3fb950;
|
||||
}
|
||||
.header-plan-usage .pu-yellow {
|
||||
color: #d6a01a;
|
||||
}
|
||||
.header-plan-usage .pu-red {
|
||||
color: #f85149;
|
||||
}
|
||||
/* Marker class must out-specify the display:inline-flex !important above. */
|
||||
.header-plan-usage.header-plan-usage--hidden {
|
||||
|
||||
Reference in New Issue
Block a user