Rehidratar Date al cargar caché y refresh no-destructivo
Bug raíz del click-no-abre-overlay: chrome.storage serializa con JSON, los Date (sessionStartTime, sessionEndTime, estimatedEndDate) volvían como strings ISO, y createWeeklyTracker llamaba .toLocaleDateString en un string. rehydrateDates los devuelve a Date al cargar. Mejoras adicionales: - showOverlayError ya no destruye el panel si hay tracker visible: saca un aviso temporal en el header (auto-oculta a los 7s). - DOM en el aviso construido con textContent, no innerHTML, para cerrar la puerta a XSS aunque el msg sea hardcodeado. - Limpieza del andamio [CUT-DIAG] usado para encontrar el bug; __claudeUsageTracker se queda expuesto para depuración futura. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
65
content.js
65
content.js
@@ -662,10 +662,34 @@
|
||||
let refreshPromise = null;
|
||||
|
||||
// -------- Cache (chrome.storage.local) --------
|
||||
// chrome.storage serializa con JSON: los Date salen como strings ISO. Al
|
||||
// cargar, rehidratamos los campos conocidos para que createWeeklyTracker
|
||||
// pueda llamar toLocaleDateString/Time sin TypeError.
|
||||
function rehydrateDates(cache) {
|
||||
if (!cache) return null;
|
||||
const toDate = (v) => {
|
||||
if (v == null) return null;
|
||||
if (v instanceof Date) return v;
|
||||
if (typeof v === 'string') {
|
||||
const d = new Date(v);
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
return v;
|
||||
};
|
||||
if (cache.pageData) {
|
||||
cache.pageData.sessionStartTime = toDate(cache.pageData.sessionStartTime);
|
||||
cache.pageData.sessionEndTime = toDate(cache.pageData.sessionEndTime);
|
||||
}
|
||||
if (cache.dailyData) {
|
||||
cache.dailyData.estimatedEndDate = toDate(cache.dailyData.estimatedEndDate);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
function loadCache() {
|
||||
return new Promise(resolve => {
|
||||
try {
|
||||
chrome.storage.local.get([CACHE_KEY], result => resolve(result[CACHE_KEY] || null));
|
||||
chrome.storage.local.get([CACHE_KEY], result => resolve(rehydrateDates(result[CACHE_KEY] || null)));
|
||||
} catch (e) {
|
||||
resolve(null);
|
||||
}
|
||||
@@ -815,12 +839,7 @@
|
||||
<span class="claude-usage-tracker-fab-text">…</span>
|
||||
`;
|
||||
fab.addEventListener('click', onFabClick);
|
||||
// DIAG: si pointerdown fira pero click no, algo está cancelando el click
|
||||
fab.addEventListener('pointerdown', () => {
|
||||
console.log('[CUT-DIAG] FAB pointerdown recibido');
|
||||
});
|
||||
document.body.appendChild(fab);
|
||||
console.log('[CUT-DIAG] FAB inyectado en DOM. id:', fab.id, 'parent:', fab.parentElement?.tagName);
|
||||
loadCache().then(updateFabState);
|
||||
}
|
||||
|
||||
@@ -842,27 +861,21 @@
|
||||
else fab.classList.remove('claude-usage-tracker-fab--hidden');
|
||||
}
|
||||
|
||||
async function onFabClick(event) {
|
||||
console.log('[CUT-DIAG] onFabClick entrada, event:', event?.type, 'target:', event?.target?.tagName);
|
||||
async function onFabClick() {
|
||||
try {
|
||||
const cache = await loadCache();
|
||||
console.log('[CUT-DIAG] cache cargada:', cache ? 'OK pageData=' + !!cache.pageData + ' dailyData=' + !!cache.dailyData : 'null');
|
||||
openOverlayPanel(cache);
|
||||
const ov = document.getElementById(OVERLAY_ID);
|
||||
console.log('[CUT-DIAG] overlay tras openOverlayPanel: existe=' + !!ov,
|
||||
ov ? 'display=' + getComputedStyle(ov).display + ' zIndex=' + getComputedStyle(ov).zIndex + ' opacity=' + getComputedStyle(ov).opacity : '');
|
||||
const ageMin = cache ? cacheAgeMin(cache.scrapedAt) : Infinity;
|
||||
if (ageMin > CACHE_TTL_MS / 60000) {
|
||||
refreshInBackground('overlay-stale');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[CUT-DIAG] onFabClick ERROR:', e);
|
||||
console.error('[Claude Usage Tracker] FAB click error:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// -------- Overlay propio --------
|
||||
function openOverlayPanel(cache) {
|
||||
console.log('[CUT-DIAG] openOverlayPanel called, cache válida:', !!(cache && cache.pageData));
|
||||
try {
|
||||
closeOverlayPanel();
|
||||
const overlay = document.createElement('div');
|
||||
@@ -904,9 +917,8 @@
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
document.addEventListener('keydown', overlayEscapeHandler);
|
||||
console.log('[CUT-DIAG] overlay appended OK. body.lastChild.id=', document.body.lastChild?.id);
|
||||
} catch (e) {
|
||||
console.error('[CUT-DIAG] openOverlayPanel ERROR:', e);
|
||||
console.error('[Claude Usage Tracker] overlay error:', e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -955,7 +967,26 @@
|
||||
const ov = document.getElementById(OVERLAY_ID);
|
||||
if (!ov) return;
|
||||
const body = ov.querySelector('.claude-usage-tracker-overlay-body');
|
||||
if (body) body.innerHTML = `<div class="claude-usage-tracker-overlay-empty">${msg}</div>`;
|
||||
const hasTracker = body && body.querySelector('.claude-usage-tracker-container');
|
||||
if (hasTracker) {
|
||||
// No destruir el tracker visible — mostrar aviso temporal en el header
|
||||
const header = ov.querySelector('.claude-usage-tracker-overlay-header');
|
||||
if (header) {
|
||||
const existing = header.querySelector('.claude-usage-tracker-overlay-notice');
|
||||
if (existing) existing.remove();
|
||||
const notice = document.createElement('span');
|
||||
notice.className = 'claude-usage-tracker-overlay-notice';
|
||||
notice.textContent = msg;
|
||||
header.appendChild(notice);
|
||||
setTimeout(() => { if (notice.parentNode) notice.remove(); }, 7000);
|
||||
}
|
||||
} else if (body) {
|
||||
body.textContent = '';
|
||||
const empty = document.createElement('div');
|
||||
empty.className = 'claude-usage-tracker-overlay-empty';
|
||||
empty.textContent = msg;
|
||||
body.appendChild(empty);
|
||||
}
|
||||
}
|
||||
|
||||
// -------- Refresh: abrir modal en background, scrapear, cerrar --------
|
||||
|
||||
12
styles.css
12
styles.css
@@ -666,6 +666,18 @@
|
||||
animation: claude-tracker-spin-pulse 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.claude-usage-tracker-overlay-notice {
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
padding: 6px 10px;
|
||||
background: rgba(245, 158, 11, 0.12);
|
||||
border: 1px solid rgba(245, 158, 11, 0.35);
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
color: #f59e0b;
|
||||
animation: claude-tracker-fade-in 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes claude-tracker-spin-pulse {
|
||||
0%, 100% { opacity: 0.6; }
|
||||
50% { opacity: 1; }
|
||||
|
||||
Reference in New Issue
Block a user