Adaptar tracker al nuevo modal de Uso

Claude movió la página de uso de /settings/usage a un modal con hash
#settings/usage sobre cualquier URL, así que el content script no se
ejecutaba en ningún sitio. Tres flujos nuevos:

- FAB flotante en claude.ai/* con uso cacheado (X% / Y%); se oculta
  mientras el modal real está abierto.
- Inyección del tracker dentro del modal real cuando el usuario lo abre,
  anclando estructuralmente sobre la fila del primer progressbar
  (locale-proof) en vez de por texto.
- Overlay propio abierto desde el FAB con caché instantánea y refresh
  en background si TTL >5 min: intenta pushState+popstate, hash,
  click en "Uso" como fallbacks; cierra con botón X o Escape.

Cache en chrome.storage.local; se calienta cuando el usuario abre el
modal por su cuenta. Observers acotados y desconectados agresivamente.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Carlos Narro
2026-06-04 13:57:11 +02:00
parent ec68336a5d
commit 673bd0bced
3 changed files with 560 additions and 70 deletions

View File

@@ -524,3 +524,166 @@
color: var(--text-300, #a3a3a3);
font-size: 12px;
}
/* ===========================================================================
Floating button (FAB) — visible en todas las páginas de claude.ai
=========================================================================== */
.claude-usage-tracker-fab {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 999998;
display: flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
background: rgba(20, 20, 22, 0.92);
color: #f5f5f5;
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 999px;
font-family: inherit;
font-size: 13px;
font-weight: 600;
cursor: pointer;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
transition: transform 0.15s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}
.claude-usage-tracker-fab:hover {
transform: translateY(-1px);
box-shadow: 0 6px 22px rgba(0, 0, 0, 0.55);
border-color: rgba(255, 255, 255, 0.22);
}
.claude-usage-tracker-fab:active {
transform: translateY(0);
}
.claude-usage-tracker-fab--hidden {
display: none !important;
}
.claude-usage-tracker-fab-icon {
font-size: 16px;
line-height: 1;
}
.claude-usage-tracker-fab-text {
font-variant-numeric: tabular-nums;
letter-spacing: 0.01em;
}
/* ===========================================================================
Overlay propio — abierto al click del FAB
=========================================================================== */
.claude-usage-tracker-overlay {
position: fixed;
inset: 0;
z-index: 999999;
background: rgba(0, 0, 0, 0.55);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
display: flex;
align-items: flex-start;
justify-content: center;
padding: 60px 16px 40px;
overflow-y: auto;
animation: claude-tracker-fade-in 0.15s ease;
}
@keyframes claude-tracker-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
.claude-usage-tracker-overlay-panel {
width: 100%;
max-width: 720px;
background: #1a1a1d;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 14px;
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
padding: 16px 20px 20px;
color: #f5f5f5;
font-family: inherit;
}
.claude-usage-tracker-overlay-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
flex-wrap: wrap;
}
.claude-usage-tracker-overlay-age {
font-size: 12px;
color: var(--text-400, #a3a3a3);
font-style: italic;
}
.claude-usage-tracker-overlay-actions {
display: flex;
gap: 8px;
margin-left: auto;
align-items: center;
}
.claude-usage-tracker-overlay-refresh,
.claude-usage-tracker-overlay-close {
background: rgba(255, 255, 255, 0.06);
color: #f5f5f5;
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 8px;
padding: 6px 12px;
font-size: 12px;
font-weight: 500;
cursor: pointer;
font-family: inherit;
transition: background 0.15s ease, border-color 0.15s ease;
}
.claude-usage-tracker-overlay-refresh:hover,
.claude-usage-tracker-overlay-close:hover {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.22);
}
.claude-usage-tracker-overlay-close {
padding: 6px 10px;
}
.claude-usage-tracker-overlay-spinner {
font-size: 12px;
color: #06b6d4;
font-style: italic;
margin-left: 8px;
animation: claude-tracker-spin-pulse 1.2s ease-in-out infinite;
}
@keyframes claude-tracker-spin-pulse {
0%, 100% { opacity: 0.6; }
50% { opacity: 1; }
}
.claude-usage-tracker-overlay-body {
min-height: 80px;
}
.claude-usage-tracker-overlay-empty {
padding: 40px 16px;
text-align: center;
color: var(--text-300, #a3a3a3);
font-size: 14px;
}
/* Cuando el tracker se inyecta dentro de nuestro overlay, ya está dentro de un
contenedor con padding/borde; quitamos su margen superior para que pegue. */
.claude-usage-tracker-overlay-body .claude-usage-tracker-container {
margin-top: 0;
}