From 2a66dd717406882c32bba3be6fd69be82a18183b Mon Sep 17 00:00:00 2001 From: s Date: Tue, 30 Jun 2026 11:33:36 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20graphes=20en=20temps=20r=C3=A9el=20sur?= =?UTF-8?q?=20le=20Farm=20Tracker=20(kills/min,=20XP/min,=20cols/min)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- renderer/index.html | 129 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/renderer/index.html b/renderer/index.html index 7f5539a..4b4b830 100644 --- a/renderer/index.html +++ b/renderer/index.html @@ -291,6 +291,14 @@ .sessions-table td.dim{font-size:12px;color:var(--text-dim);} .hist-empty{padding:40px 0;text-align:center;color:var(--text-dim);font-size:14px;line-height:1.8;} @media(max-width:760px){.charts-2col{grid-template-columns:1fr;}} + + /* ---------- Graphes en direct ---------- */ + .charts-3col{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;} + @media(max-width:760px){.charts-3col{grid-template-columns:1fr;}} + .live-chart-header{display:flex;justify-content:space-between;align-items:baseline; + font-family:var(--font-mono);font-size:11px;margin-bottom:5px;} + .live-chart-header .chart-label{color:var(--text-dim);} + .live-chart-header .chart-val{font-size:12px;font-weight:600;} @@ -394,6 +402,38 @@

Le jeu ne transmet pas les ventes : fixe un prix par item dans le tableau « Loot » ci-dessous. Tes prix sont sauvegardés automatiquement.

+ +

Stats de farm — cette session

@@ -875,7 +915,9 @@ itemRows: new Map(), mobFilter: '', itemFilter: '', - selectedDropMob: null + selectedDropMob: null, + timeline: [], + timelineTimer: null }; // Retourne les stats à afficher : communauté si dispo et activé, sinon perso @@ -936,6 +978,8 @@ renderAll(); startPolling(); startClock(); + if (resetCounters) state.timeline = []; + startTimelineTimer(); persistAppState(true); } @@ -945,6 +989,7 @@ state.paused = true; state.pauseStartedAt = Date.now(); stopPolling(); + stopTimelineTimer(); setStatus('paused', 'En pause'); pauseBtn.textContent = '▶ Reprendre'; flashMessage('Suivi en pause — le chrono et les cadences sont gelés.'); @@ -958,6 +1003,7 @@ setStatus('live', 'En direct — ' + state.logPath); pauseBtn.textContent = '⏸ Pause'; startPolling(); + startTimelineTimer(); flashMessage('Suivi repris.'); } }); @@ -977,9 +1023,13 @@ state.pausedAccum = 0; if (state.paused) state.pauseStartedAt = Date.now(); state.lastEventAt = null; + state.timeline = []; const sizeRes = await window.api.getFileSize(state.logPath); state.lastSize = sizeRes.size || 0; state.carry = ''; + $('liveChartsPanel').hidden = true; + stopTimelineTimer(); + if (!state.paused) startTimelineTimer(); renderAll(); persistAppState(true); flashMessage('Session réinitialisée (les chances de drop ne sont pas affectées).'); @@ -1222,6 +1272,83 @@ updateClock(); } function pad(n){ return String(n).padStart(2,'0'); } + + // ---------- Timeline / graphes en direct ---------- + + function snapshotTimeline(){ + if (!state.sessionStart || state.paused) return; + const totalKills = sumValues(state.mobCounts); + state.timeline.push({ t: Date.now(), kills: totalKills, exp: state.expTotal, cols: state.killCols }); + if (state.timeline.length > 180) state.timeline.shift(); + renderLiveCharts(); + } + function startTimelineTimer(){ + stopTimelineTimer(); + snapshotTimeline(); + state.timelineTimer = setInterval(snapshotTimeline, 30000); + } + function stopTimelineTimer(){ + if (state.timelineTimer){ clearInterval(state.timelineTimer); state.timelineTimer = null; } + } + + function drawLiveChart(container, rates, colorHex){ + if (rates.length < 2){ + container.innerHTML = '

En attente…

'; + return; + } + const W=400,H=55,padL=4,padR=4,padT=6,padB=4; + const plotW=W-padL-padR,plotH=H-padT-padB,n=rates.length; + const maxV=Math.max(...rates,0.01); + const xs=rates.map((_,i)=>padL+(n===1?plotW/2:(i/(n-1))*plotW)); + const ys=rates.map(v=>padT+plotH-(Math.max(0,v)/maxV)*plotH); + const linePath=xs.map((x,i)=>(i===0?'M':'L')+x.toFixed(1)+','+ys[i].toFixed(1)).join(' '); + const areaPath=linePath+' L'+xs[n-1].toFixed(1)+','+(padT+plotH)+' L'+xs[0].toFixed(1)+','+(padT+plotH)+' Z'; + const grid=[0.5,1.0].map(pct=>{ + const y=(padT+plotH*(1-pct)).toFixed(1); + return ''; + }).join(''); + container.innerHTML= + '' + +grid + +'' + +'' + +''; + } + + function fmtHHMM(ts){ const d=new Date(ts); return pad(d.getHours())+':'+pad(d.getMinutes()); } + + function renderLiveCharts(){ + const snap = state.timeline; + const panel = $('liveChartsPanel'); + if (snap.length < 1){ panel.hidden = true; return; } + panel.hidden = false; + + const killRates=[], expRates=[], colRates=[]; + for (let i=1; i arr.length ? arr[arr.length-1] : 0; + $('liveChartKillsVal').textContent = fmt(last(killRates),1)+'/min'; + $('liveChartExpVal').textContent = fmt(last(expRates),1)+'/min'; + $('liveChartColsVal').textContent = fmt(last(colRates),1)+'/min'; + + if (snap.length >= 2){ + const t1=fmtHHMM(snap[0].t), t2=fmtHHMM(snap[snap.length-1].t); + ['KillsTime','ExpTime','ColsTime'].forEach(id=>{ + const el=$('liveChart'+id); + if (el) el.innerHTML=''+t1+''+t2+''; + }); + } + } function sumValues(map){ let t=0; for (const v of map.values()) t+=v; return t; } async function saveCurrentSessionToHistory(){