mejora semanal
This commit is contained in:
175
content.js
175
content.js
@@ -195,13 +195,22 @@
|
||||
data.weeklyResetText = resetText;
|
||||
|
||||
// Parsear "Se restablece vie, 4:59" para obtener día y hora
|
||||
const resetMatch = resetText.match(/Se restablece\s+(\w+),?\s*([\d:]+)/i);
|
||||
const resetMatch = 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
|
||||
data.resetDayIndex = DAYS_ES.findIndex(d => d === dayAbbr || d.startsWith(dayAbbr.substring(0, 3)));
|
||||
// Encontrar el índice del día (comparar primeras 3 letras sin acentos)
|
||||
const normalize = s => s.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
||||
const dayAbbrNorm = normalize(dayAbbr);
|
||||
data.resetDayIndex = DAYS_ES.findIndex(d => {
|
||||
const dNorm = normalize(d);
|
||||
return dNorm === dayAbbrNorm || dNorm.startsWith(dayAbbrNorm.substring(0, 3)) || dayAbbrNorm.startsWith(dNorm.substring(0, 3));
|
||||
});
|
||||
|
||||
console.log('[Claude Usage Tracker] Reset parsed:', dayAbbr, '->', data.resetDayIndex, 'hour:', data.resetHour);
|
||||
} else {
|
||||
console.log('[Claude Usage Tracker] Reset text no match:', resetText);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,6 +245,7 @@
|
||||
/**
|
||||
* Calcula el desglose diario basado en los datos reales
|
||||
* Usa HORAS transcurridas para mayor precisión
|
||||
* Divide el día de reinicio en dos partes: inicio (después del reinicio) y fin (antes del reinicio)
|
||||
*/
|
||||
function calculateDailyUsage(pageData) {
|
||||
const { weeklyUsage, resetDayIndex, resetHour } = pageData;
|
||||
@@ -246,14 +256,16 @@
|
||||
|
||||
// Calcular el momento exacto del último reinicio
|
||||
const [resetH, resetM] = (resetHour || '5:00').split(':').map(Number);
|
||||
const resetHourDecimal = resetH + (resetM || 0) / 60;
|
||||
|
||||
// Encontrar la fecha del último reinicio
|
||||
// Encontrar la fecha del último reinicio (usar effectiveResetDayIndex más abajo)
|
||||
const effectiveResetIdx = resetDayIndex >= 0 ? resetDayIndex : 5;
|
||||
let lastResetDate = new Date(now);
|
||||
let daysSinceReset = todayIndex - resetDayIndex;
|
||||
let daysSinceReset = todayIndex - effectiveResetIdx;
|
||||
if (daysSinceReset < 0) daysSinceReset += 7;
|
||||
if (daysSinceReset === 0) {
|
||||
// Es el día de reinicio, ¿ya pasó la hora?
|
||||
if (now.getHours() < resetH || (now.getHours() === resetH && now.getMinutes() < resetM)) {
|
||||
if (currentHour < resetHourDecimal) {
|
||||
daysSinceReset = 7; // Aún no ha reiniciado, usar el de la semana pasada
|
||||
}
|
||||
}
|
||||
@@ -279,88 +291,126 @@
|
||||
const avgUsagePerHour = hoursElapsed > 0 ? weeklyUsage / hoursElapsed : 0;
|
||||
const idealUsagePerHour = 100 / totalWeekHours;
|
||||
|
||||
// La semana empieza el mismo día del reinicio (ej: viernes)
|
||||
// El primer día es el día del reinicio, desde la hora de reinicio
|
||||
const startDayIndex = resetDayIndex;
|
||||
|
||||
// Calcular la posición del día actual dentro de la semana de facturación
|
||||
// Posición 0 = día del reinicio, posición 1 = día siguiente, etc.
|
||||
let todayPositionInWeek = todayIndex - resetDayIndex;
|
||||
if (todayPositionInWeek < 0) todayPositionInWeek += 7;
|
||||
// Nombre del día de reinicio (con fallback, reutilizar effectiveResetIdx)
|
||||
const effectiveResetDayIndex = effectiveResetIdx;
|
||||
const resetDayName = DAYS_ES_DISPLAY[effectiveResetDayIndex] || 'Vie';
|
||||
const resetDayFullName = DAYS_FULL_ES[effectiveResetDayIndex] || 'Viernes';
|
||||
|
||||
const dailyBreakdown = [];
|
||||
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const dayIndex = (startDayIndex + i) % 7;
|
||||
const isToday = (i === todayPositionInWeek);
|
||||
const isPast = (i < todayPositionInWeek);
|
||||
const isFuture = (i > todayPositionInWeek);
|
||||
// Estructura: 8 segmentos
|
||||
// 0: Día reinicio (inicio) - desde resetHour hasta 24:00
|
||||
// 1-6: Días completos (sáb, dom, lun, mar, mié, jue si reinicio es viernes)
|
||||
// 7: Día reinicio (fin) - desde 00:00 hasta resetHour
|
||||
|
||||
// Calcular cuántas horas de este día han transcurrido
|
||||
for (let i = 0; i < 8; i++) {
|
||||
let dayIndex, dayName, dayFullName, dayTotalHours, isResetDayStart, isResetDayEnd;
|
||||
|
||||
if (i === 0) {
|
||||
// Primer segmento: día de reinicio (parte después del reinicio)
|
||||
dayIndex = effectiveResetDayIndex;
|
||||
dayName = resetDayName;
|
||||
dayFullName = resetDayFullName + ' (inicio)';
|
||||
dayTotalHours = 24 - resetHourDecimal;
|
||||
isResetDayStart = true;
|
||||
isResetDayEnd = false;
|
||||
} else if (i === 7) {
|
||||
// Último segmento: día de reinicio (parte antes del reinicio)
|
||||
dayIndex = effectiveResetDayIndex;
|
||||
dayName = resetDayName;
|
||||
dayFullName = resetDayFullName + ' (fin)';
|
||||
dayTotalHours = resetHourDecimal;
|
||||
isResetDayStart = false;
|
||||
isResetDayEnd = true;
|
||||
} else {
|
||||
// Días intermedios (completos)
|
||||
dayIndex = (effectiveResetDayIndex + i) % 7;
|
||||
dayName = DAYS_ES_DISPLAY[dayIndex];
|
||||
dayFullName = DAYS_FULL_ES[dayIndex];
|
||||
dayTotalHours = 24;
|
||||
isResetDayStart = false;
|
||||
isResetDayEnd = false;
|
||||
}
|
||||
|
||||
// Calcular posición del día actual
|
||||
let segmentPosition;
|
||||
if (todayIndex === effectiveResetDayIndex) {
|
||||
// Hoy es el día de reinicio
|
||||
if (currentHour >= resetHourDecimal) {
|
||||
segmentPosition = 0; // Estamos en la parte de inicio
|
||||
} else {
|
||||
segmentPosition = 7; // Estamos en la parte de fin (antes del reinicio)
|
||||
}
|
||||
} else {
|
||||
// Día normal
|
||||
let pos = todayIndex - effectiveResetDayIndex;
|
||||
if (pos <= 0) pos += 7;
|
||||
segmentPosition = pos;
|
||||
}
|
||||
|
||||
const isToday = (i === segmentPosition);
|
||||
const isPast = (i < segmentPosition);
|
||||
const isFuture = (i > segmentPosition);
|
||||
|
||||
// Calcular horas transcurridas en este segmento
|
||||
let dayHoursElapsed = 0;
|
||||
|
||||
if (isPast) {
|
||||
// Día completo ya pasado
|
||||
if (i === 0) {
|
||||
// Primer día (día del reinicio): desde hora de reinicio hasta medianoche
|
||||
dayHoursElapsed = 24 - (resetH + (resetM || 0) / 60);
|
||||
} else {
|
||||
dayHoursElapsed = 24;
|
||||
}
|
||||
dayHoursElapsed = dayTotalHours;
|
||||
} else if (isToday) {
|
||||
if (i === 0) {
|
||||
// Hoy es el día del reinicio: desde hora de reinicio hasta ahora
|
||||
dayHoursElapsed = currentHour - (resetH + (resetM || 0) / 60);
|
||||
// Hoy es el día de reinicio, parte inicio
|
||||
dayHoursElapsed = currentHour - resetHourDecimal;
|
||||
if (dayHoursElapsed < 0) dayHoursElapsed = 0;
|
||||
} else if (i === 7) {
|
||||
// Hoy es el día de reinicio, parte fin
|
||||
dayHoursElapsed = currentHour;
|
||||
} else {
|
||||
// Día actual normal: desde medianoche hasta ahora
|
||||
// Día normal
|
||||
dayHoursElapsed = currentHour;
|
||||
}
|
||||
}
|
||||
// Días futuros: dayHoursElapsed = 0
|
||||
|
||||
// Calcular horas totales del día (para el porcentaje de llenado)
|
||||
let dayTotalHours = 24;
|
||||
if (i === 0) {
|
||||
// Primer día: solo cuenta desde la hora de reinicio
|
||||
dayTotalHours = 24 - (resetH + (resetM || 0) / 60);
|
||||
}
|
||||
|
||||
// Porcentaje de llenado del día (0-100%)
|
||||
// Porcentaje de llenado del segmento (0-100%)
|
||||
const dayFillPercent = dayTotalHours > 0 ? (dayHoursElapsed / dayTotalHours) * 100 : 0;
|
||||
|
||||
// Uso asignado a este día (promedio distribuido)
|
||||
// Uso asignado a este segmento (promedio distribuido)
|
||||
let usage = 0;
|
||||
if (dayHoursElapsed > 0 && hoursElapsed > 0) {
|
||||
// Distribuir el uso total proporcionalmente a las horas transcurridas
|
||||
usage = (dayHoursElapsed / hoursElapsed) * weeklyUsage;
|
||||
}
|
||||
|
||||
// Estado basado en si el uso del día está dentro del ideal
|
||||
// Estado basado en si el uso está dentro del ideal
|
||||
let status = 'pending';
|
||||
if (dayHoursElapsed > 0) {
|
||||
const idealForThisDay = (dayHoursElapsed / 24) * idealDailyPercent;
|
||||
status = usage <= idealForThisDay * 1.1 ? 'good' :
|
||||
usage <= idealForThisDay * 1.5 ? 'warning' : 'danger';
|
||||
const idealForThisSegment = (dayHoursElapsed / totalWeekHours) * 100;
|
||||
const actualForThisSegment = usage;
|
||||
status = actualForThisSegment <= idealForThisSegment * 1.1 ? 'good' :
|
||||
actualForThisSegment <= idealForThisSegment * 1.5 ? 'warning' : 'danger';
|
||||
}
|
||||
|
||||
// Offset para el primer día (horas antes del reinicio que no cuentan)
|
||||
const dayStartOffsetPercent = (i === 0) ? ((resetH + (resetM || 0) / 60) / 24) * 100 : 0;
|
||||
// Offset visual (solo para el segmento de inicio del día de reinicio)
|
||||
const dayStartOffsetPercent = isResetDayStart ? (resetHourDecimal / 24) * 100 : 0;
|
||||
// Offset final (solo para el segmento de fin del día de reinicio)
|
||||
const dayEndOffsetPercent = isResetDayEnd ? ((24 - resetHourDecimal) / 24) * 100 : 0;
|
||||
|
||||
dailyBreakdown.push({
|
||||
dayIndex,
|
||||
dayName: DAYS_ES_DISPLAY[dayIndex],
|
||||
dayFullName: DAYS_FULL_ES[dayIndex],
|
||||
dayName,
|
||||
dayFullName,
|
||||
isPast,
|
||||
isToday,
|
||||
isFuture,
|
||||
isResetDay: i === 0,
|
||||
isResetDayStart,
|
||||
isResetDayEnd,
|
||||
usage,
|
||||
idealUsage: idealDailyPercent,
|
||||
dayFillPercent, // % del día transcurrido (para llenar la barra)
|
||||
idealUsage: (dayTotalHours / totalWeekHours) * 100,
|
||||
dayFillPercent,
|
||||
dayHoursElapsed,
|
||||
dayTotalHours,
|
||||
dayStartOffsetPercent, // % del día antes del reinicio (hueco a la izquierda)
|
||||
dayStartOffsetPercent,
|
||||
dayEndOffsetPercent,
|
||||
status
|
||||
});
|
||||
}
|
||||
@@ -410,7 +460,7 @@
|
||||
container.id = 'claude-usage-tracker';
|
||||
|
||||
const usagePercent = pageData.weeklyUsage;
|
||||
const resetDayName = DAYS_FULL_ES[pageData.resetDayIndex] || 'próximo reinicio';
|
||||
const resetDayName = DAYS_FULL_ES[pageData.resetDayIndex >= 0 ? pageData.resetDayIndex : 5] || 'próximo reinicio';
|
||||
const boostStatus = getBoostStatus();
|
||||
|
||||
container.innerHTML = `
|
||||
@@ -434,21 +484,22 @@
|
||||
|
||||
<div class="weekly-progress-container">
|
||||
${dailyData.dailyBreakdown.map(day => `
|
||||
<div class="day-segment ${day.isToday ? 'today' : ''} ${day.isFuture ? 'future' : ''} ${day.isResetDay ? 'reset-day' : ''}">
|
||||
<div class="day-segment ${day.isToday ? 'today' : ''} ${day.isFuture ? 'future' : ''} ${day.isResetDayStart ? 'reset-day-start' : ''} ${day.isResetDayEnd ? 'reset-day-end' : ''}">
|
||||
<div class="day-tooltip">
|
||||
${day.dayFullName}: ${day.usage.toFixed(1)}% usado
|
||||
${day.isToday ? ` (${day.dayHoursElapsed.toFixed(1)}h transcurridas)` : day.isPast ? ' (completo)' : ''}
|
||||
${day.isResetDay ? `<br>Reinicio a las ${pageData.resetHour}` : ''}
|
||||
<br>Ideal para ${day.dayHoursElapsed.toFixed(0)}h: ${((day.dayHoursElapsed / 24) * day.idealUsage).toFixed(1)}%
|
||||
${day.dayFullName}: ${day.usage.toFixed(1)}% usado (${day.dayTotalHours.toFixed(1)}h)
|
||||
${day.isToday ? ` — ${day.dayHoursElapsed.toFixed(1)}h transcurridas` : day.isPast ? ' — completo' : ''}
|
||||
${day.isResetDayStart ? `<br>🔄 Desde reinicio (${pageData.resetHour})` : ''}
|
||||
${day.isResetDayEnd ? `<br>⏳ Hasta reinicio (${pageData.resetHour})` : ''}
|
||||
</div>
|
||||
<div class="day-bar">
|
||||
${day.isResetDay ? `<div class="day-bar-offset" style="width: ${day.dayStartOffsetPercent}%"></div>` : ''}
|
||||
<div class="day-bar-bg" style="left: ${day.dayStartOffsetPercent}%; width: ${(100 - day.dayStartOffsetPercent) * day.dayFillPercent / 100}%"></div>
|
||||
${day.isResetDayStart ? `<div class="day-bar-offset start" style="width: ${day.dayStartOffsetPercent}%"></div>` : ''}
|
||||
${day.isResetDayEnd ? `<div class="day-bar-offset end" style="left: ${100 - day.dayEndOffsetPercent}%; width: ${day.dayEndOffsetPercent}%"></div>` : ''}
|
||||
<div class="day-bar-bg" style="left: ${day.dayStartOffsetPercent}%; width: ${day.dayFillPercent * (100 - day.dayStartOffsetPercent - day.dayEndOffsetPercent) / 100}%"></div>
|
||||
<div class="day-bar-fill ${day.status === 'good' ? 'under-budget' : day.status === 'warning' ? 'on-track' : day.status === 'danger' ? 'over-budget' : ''}"
|
||||
style="left: ${day.dayStartOffsetPercent}%; width: ${(100 - day.dayStartOffsetPercent) * day.dayFillPercent / 100}%">
|
||||
style="left: ${day.dayStartOffsetPercent}%; width: ${day.dayFillPercent * (100 - day.dayStartOffsetPercent - day.dayEndOffsetPercent) / 100}%">
|
||||
</div>
|
||||
</div>
|
||||
<span class="day-label">${day.dayName}</span>
|
||||
<span class="day-label">${day.isResetDayStart ? day.dayName + '↓' : day.isResetDayEnd ? day.dayName + '↑' : day.dayName}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
|
||||
15
styles.css
15
styles.css
@@ -87,9 +87,18 @@
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.day-segment.reset-day .day-label::after {
|
||||
content: ' 🔄';
|
||||
font-size: 8px;
|
||||
.day-segment.reset-day-start .day-label {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.day-segment.reset-day-end .day-label {
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.day-bar-offset.end {
|
||||
left: auto;
|
||||
right: 0;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.day-bar-fill.under-budget {
|
||||
|
||||
Reference in New Issue
Block a user