diff --git a/public/app.js b/public/app.js index 678ccd8..d297d5d 100644 --- a/public/app.js +++ b/public/app.js @@ -209,11 +209,42 @@ async function fetchHistory() { const response = await fetch('/api/history'); const history = await response.json(); - const labels = [...new Set(history.map(item => new Date(item.timestamp).toLocaleTimeString()))].sort(); + // Group by 5-minute intervals to align points from the same fetch batch + const snapshots = {}; + history.forEach(item => { + const time = new Date(item.timestamp); + const coeff = 1000 * 60 * 5; + const roundedTime = new Date(Math.round(time.getTime() / coeff) * coeff); + const timeKey = roundedTime.getTime(); + + if (!snapshots[timeKey]) { + snapshots[timeKey] = { + label: roundedTime.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}), + data: {} + }; + } + snapshots[timeKey].data[item.provider] = item.percentage; + }); + + const sortedKeys = Object.keys(snapshots).sort((a, b) => a - b); + const labels = sortedKeys.map(k => snapshots[k].label); - const antigravityData = history.filter(item => item.provider === 'Antigravity').map(item => item.percentage); - const poeData = history.filter(item => item.provider === 'POE').map(item => item.percentage); - const minimaxData = history.filter(item => item.provider === 'MiniMax').map(item => item.percentage); + const datasetsConfig = [ + { label: 'AG Gemini (5h)', provider: 'AG Gemini (5h)', color: '#58a6ff' }, + { label: 'AG Claude (5h)', provider: 'AG Claude (5h)', color: '#8957e5' }, + { label: 'POE', provider: 'POE', color: '#3fb950' }, + { label: 'MiniMax (5h)', provider: 'MiniMax (5h)', color: '#d29922' } + ]; + + const datasets = datasetsConfig.map(config => ({ + label: config.label, + data: sortedKeys.length ? sortedKeys.map(k => snapshots[k].data[config.provider] || 0) : [0, 0, 0, 0], + borderColor: config.color, + backgroundColor: config.color + '1a', // will be overridden by theme + borderWidth: 2, + tension: 0.3, + fill: true + })); const ctx = document.getElementById('historyChart').getContext('2d'); @@ -221,35 +252,7 @@ async function fetchHistory() { type: 'line', data: { labels: labels.length ? labels : ['10:00', '11:00', '12:00', '13:00'], - datasets: [ - { - label: 'Antigravity (%)', - data: antigravityData.length ? antigravityData : [10, 30, 50, 68], - borderColor: '#58a6ff', - backgroundColor: 'rgba(88, 166, 255, 0.1)', - borderWidth: 2, - tension: 0.3, - fill: true - }, - { - label: 'POE (%)', - data: poeData.length ? poeData : [5, 10, 20, 45], - borderColor: '#3fb950', - backgroundColor: 'rgba(63, 185, 80, 0.1)', - borderWidth: 2, - tension: 0.3, - fill: true - }, - { - label: 'MiniMax (%)', - data: minimaxData.length ? minimaxData : [0, 0, 30, 60], - borderColor: '#d29922', - backgroundColor: 'rgba(210, 153, 34, 0.1)', - borderWidth: 2, - tension: 0.3, - fill: true - } - ] + datasets: datasets }, options: { responsive: true, @@ -284,20 +287,20 @@ async function fetchHistory() { function updateChartTheme(themeClass) { if (!historyChart) return; - let colors = ['#58a6ff', '#3fb950', '#d29922']; // Default + let colors = ['#58a6ff', '#8957e5', '#3fb950', '#d29922']; // Default switch (themeClass) { case 'style-pacman': - colors = ['#f1c40f', '#f39c12', '#e67e22']; + colors = ['#f1c40f', '#f39c12', '#e67e22', '#d35400']; break; case 'style-nyancat': - colors = ['#ff0000', '#33ff00', '#0099ff']; + colors = ['#ff0000', '#ff9900', '#33ff00', '#0099ff']; break; case 'style-matrix': - colors = ['#0f0', '#0a0', '#050']; + colors = ['#0f0', '#0c0', '#0a0', '#050']; break; case 'style-lava': - colors = ['#ff4e00', '#ec9f05', '#bf3a00']; + colors = ['#ff4e00', '#ff7a00', '#ec9f05', '#bf3a00']; break; }