From 6e5f58915656b04322d9a51fa90c4c8c3c365915 Mon Sep 17 00:00:00 2001 From: Carlos Narro Date: Thu, 4 Jun 2026 14:45:00 +0200 Subject: [PATCH] =?UTF-8?q?Rehidratar=20Date=20al=20cargar=20cach=C3=A9=20?= =?UTF-8?q?y=20refresh=20no-destructivo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- content.js | 65 ++++++++++++++++++++++++++++++++++++++++-------------- styles.css | 12 ++++++++++ 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/content.js b/content.js index f0478f8..eb686a1 100644 --- a/content.js +++ b/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 @@ `; 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 = `
${msg}
`; + 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 -------- diff --git a/styles.css b/styles.css index 47b1753..2e5b7e7 100644 --- a/styles.css +++ b/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; }