Arreglar parser: anclar en texto/role en vez de clases CSS
La página de uso de Claude eliminó el design token text-text-100,
lo que rompía la búsqueda de filas por p.font-base.text-text-100 y
dejaba al tracker sin inyectarse. Reescrito parsePageData para
localizar cada métrica por su etiqueta de texto ("Todos los modelos",
"Sesión actual"), subir hasta el ancestro con [role=progressbar] y
leer aria-valuenow + texto "Se restablece". Robusto ante renombrados
de clases de Claude.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
95
content.js
95
content.js
@@ -138,6 +138,52 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encuentra el elemento "hoja" cuyo texto coincide exactamente con el texto dado.
|
||||
* Se ancla en el TEXTO, no en clases CSS, para ser robusto ante los cambios
|
||||
* de design tokens de Claude (p.ej. text-text-100 desapareció en mayo 2026).
|
||||
*/
|
||||
function findLabelElement(text) {
|
||||
let best = null;
|
||||
const candidates = document.querySelectorAll('p, span, div, h2, h3, h4');
|
||||
for (const el of candidates) {
|
||||
if (el.textContent.trim() === text) {
|
||||
// Preferir el match más interno (menor longitud de texto = más cercano a la hoja)
|
||||
if (!best || el.textContent.length <= best.textContent.length) {
|
||||
best = el;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dada la etiqueta de una métrica (p.ej. "Todos los modelos" o "Sesión actual"),
|
||||
* localiza su fila subiendo desde la etiqueta hasta el primer ancestro que
|
||||
* contiene un [role="progressbar"], y devuelve { usage, resetText }.
|
||||
* No depende de nombres de clase CSS de Claude.
|
||||
*/
|
||||
function getMetricByLabel(labelText) {
|
||||
const labelEl = findLabelElement(labelText);
|
||||
if (!labelEl) return null;
|
||||
|
||||
let container = labelEl;
|
||||
for (let i = 0; i < 10 && container; i++) {
|
||||
if (container.querySelector('[role="progressbar"]')) break;
|
||||
container = container.parentElement;
|
||||
}
|
||||
|
||||
const progressBar = container && container.querySelector('[role="progressbar"]');
|
||||
if (!progressBar) return null;
|
||||
|
||||
const usage = parseFloat(progressBar.getAttribute('aria-valuenow')) || 0;
|
||||
const containerText = container.innerText || container.textContent || '';
|
||||
const resetMatch = containerText.match(/Se restablece[^\n]*/);
|
||||
const resetText = resetMatch ? resetMatch[0].trim() : '';
|
||||
|
||||
return { usage, resetText };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsea la información de la página real de Claude
|
||||
* Busca los progressbar con aria-valuenow y el texto "Se restablece..."
|
||||
@@ -157,29 +203,14 @@
|
||||
daysPassed: 0
|
||||
};
|
||||
|
||||
// Buscar todas las secciones con progressbar
|
||||
const sections = document.querySelectorAll('section');
|
||||
|
||||
for (const section of sections) {
|
||||
const rows = section.querySelectorAll('.flex.flex-row');
|
||||
|
||||
for (const row of rows) {
|
||||
const labelEl = row.querySelector('p.font-base.text-text-100');
|
||||
const resetTextEl = row.querySelector('p.font-base.text-text-400');
|
||||
const progressBar = row.querySelector('[role="progressbar"]');
|
||||
|
||||
if (!labelEl || !progressBar) continue;
|
||||
|
||||
const label = labelEl.textContent.trim();
|
||||
const usage = parseFloat(progressBar.getAttribute('aria-valuenow')) || 0;
|
||||
const resetText = resetTextEl ? resetTextEl.textContent.trim() : '';
|
||||
|
||||
if (label === 'Sesión actual') {
|
||||
data.sessionUsage = usage;
|
||||
data.sessionResetText = resetText;
|
||||
// Sesión actual
|
||||
const session = getMetricByLabel('Sesión actual');
|
||||
if (session) {
|
||||
data.sessionUsage = session.usage;
|
||||
data.sessionResetText = session.resetText;
|
||||
|
||||
// Calcular tiempo restante y horas de inicio/fin
|
||||
data.sessionTimeRemainingMin = parseTimeRemaining(resetText);
|
||||
data.sessionTimeRemainingMin = parseTimeRemaining(session.resetText);
|
||||
if (data.sessionTimeRemainingMin > 0) {
|
||||
const now = new Date();
|
||||
const sessionDurationMin = SESSION_DURATION_HOURS * 60;
|
||||
@@ -190,18 +221,24 @@
|
||||
// Hora de fin = ahora + tiempo restante
|
||||
data.sessionEndTime = new Date(now.getTime() + data.sessionTimeRemainingMin * 60 * 1000);
|
||||
}
|
||||
} else if (label === 'Todos los modelos') {
|
||||
data.weeklyUsage = usage;
|
||||
data.weeklyResetText = resetText;
|
||||
} else {
|
||||
console.log('[Claude Usage Tracker] No se encontró la métrica "Sesión actual"');
|
||||
}
|
||||
|
||||
// Todos los modelos (uso semanal)
|
||||
const weekly = getMetricByLabel('Todos los modelos');
|
||||
if (weekly) {
|
||||
data.weeklyUsage = weekly.usage;
|
||||
data.weeklyResetText = weekly.resetText;
|
||||
|
||||
// Parsear "Se restablece vie, 4:59" para obtener día y hora
|
||||
const resetMatch = resetText.match(/Se restablece\s+([a-záéíóúñü]+),?\s*([\d:]+)/i);
|
||||
const resetMatch = weekly.resetText.match(/Se restablece\s+([a-záéíóúñü]+),?\s*([\d:]+)/i);
|
||||
if (resetMatch) {
|
||||
const dayAbbr = resetMatch[1].toLowerCase();
|
||||
data.resetHour = resetMatch[2];
|
||||
|
||||
// Encontrar el índice del día (comparar primeras 3 letras sin acentos)
|
||||
const normalize = s => s.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
||||
const normalize = s => s.normalize('NFD').replace(/[̀-ͯ]/g, '');
|
||||
const dayAbbrNorm = normalize(dayAbbr);
|
||||
data.resetDayIndex = DAYS_ES.findIndex(d => {
|
||||
const dNorm = normalize(d);
|
||||
@@ -210,10 +247,10 @@
|
||||
|
||||
console.log('[Claude Usage Tracker] Reset parsed:', dayAbbr, '->', data.resetDayIndex, 'hour:', data.resetHour);
|
||||
} else {
|
||||
console.log('[Claude Usage Tracker] Reset text no match:', resetText);
|
||||
}
|
||||
}
|
||||
console.log('[Claude Usage Tracker] Reset text no match:', weekly.resetText);
|
||||
}
|
||||
} else {
|
||||
console.log('[Claude Usage Tracker] No se encontró la métrica "Todos los modelos"');
|
||||
}
|
||||
|
||||
// Calcular días hasta el reinicio y días pasados
|
||||
|
||||
Reference in New Issue
Block a user