Displays Claude.ai session and weekly usage stats as a floating overlay while you chat.
// ==UserScript==
// @name Claude Usage Overlay
// @namespace https://greasyfork.org/users/tac-tac-go
// @version 1.0.0
// @description Displays Claude.ai session and weekly usage stats as a floating overlay while you chat.
// @author tac-tac-go
// @match https://claude.ai/*
// @grant none
// @license MIT
// @homepageURL https://greasyfork.org/users/tac-tac-go
// @supportURL https://greasyfork.org/users/tac-tac-go
// ==/UserScript==
(function () {
'use strict';
function loadUsageViaIframe() {
return new Promise((resolve) => {
document.getElementById('usage-iframe')?.remove();
const iframe = document.createElement('iframe');
iframe.id = 'usage-iframe';
iframe.src = '/settings/usage';
iframe.style.cssText = 'position:fixed;top:-9999px;left:-9999px;width:1px;height:1px;';
document.body.appendChild(iframe);
iframe.onload = () => {
let attempts = 0;
const timer = setInterval(() => {
attempts++;
const iDoc = iframe.contentDocument;
const bars = iDoc?.querySelectorAll('[role="progressbar"]');
if (bars?.length >= 1) {
clearInterval(timer);
iframe.remove();
const sessionPct = bars[0]?.getAttribute('aria-valuenow') ?? '?';
const weeklyPct = bars[1]?.getAttribute('aria-valuenow') ?? '?';
const allP = Array.from(iDoc.querySelectorAll('p'));
const resetTexts = allP.filter(p => p.textContent.includes('にリセット'));
resolve({
sessionPct,
weeklyPct,
sessionReset: resetTexts[0]?.textContent.trim() ?? '',
weeklyReset: resetTexts[1]?.textContent.trim() ?? '',
});
}
if (attempts > 20) {
clearInterval(timer);
iframe.remove();
resolve(null);
}
}, 200);
};
});
}
async function updateUsage() {
const el = document.getElementById('claude-usage-overlay');
el.textContent = 'Updating...';
const data = await loadUsageViaIframe();
if (!data) {
el.textContent = 'Failed to load';
return;
}
el.innerHTML = `
<div style="font-weight:bold;margin-bottom:6px">📊 Claude Usage</div>
<div>🕐 Session: <b>${data.sessionPct}%</b></div>
<div style="font-size:10px;color:#aaa;margin-bottom:4px">${data.sessionReset}</div>
<div>📅 Weekly: <b>${data.weeklyPct}%</b></div>
<div style="font-size:10px;color:#aaa">${data.weeklyReset}</div>
<div style="font-size:10px;color:#555;margin-top:6px">Click to refresh</div>
`;
}
const overlay = document.createElement('div');
overlay.id = 'claude-usage-overlay';
overlay.style.cssText = `
position:fixed; bottom:80px; right:16px; z-index:9999;
background:#1e1e1e; color:#e0e0e0; padding:10px 14px;
border-radius:8px; font-size:12px; border:1px solid #444;
min-width:160px; line-height:1.6; cursor:pointer;
box-shadow: 0 2px 8px rgba(0,0,0,0.4);
`;
overlay.textContent = 'Loading...';
overlay.title = 'Click to refresh';
overlay.onclick = updateUsage;
document.body.appendChild(overlay);
updateUsage();
setInterval(updateUsage, 5 * 60 * 1000);
})();