diff --git a/package.json b/package.json index fa4f021..384b86a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "multi-hoster-uploader", - "version": "3.3.99", + "version": "3.3.100", "description": "Upload files to doodstream, voe, vidmoly, byse simultaneously", "main": "main.js", "scripts": { diff --git a/renderer/app.js b/renderer/app.js index 9b469db..bbba33b 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -20,13 +20,38 @@ let uploading = false; let healthCheckRunning = false; let _rLongTasks = 0, _rLongTaskMax = 0, _rFrameLast = 0, _rFrameWorst = 0, _rFrameCount = 0, _rFrameJank = 0, _rPerfLastLog = 0, _rPerfWindowStart = 0; +function _rElLabel(el) { + try { + if (!el || !el.tagName) return '?'; + let s = el.tagName.toLowerCase(); + if (el.id) s += '#' + el.id; + else if (el.className && typeof el.className === 'string') { const c = el.className.trim().split(/\s+/)[0]; if (c) s += '.' + c; } + const a = el.getAttribute && (el.getAttribute('data-action') || el.getAttribute('data-tab') || el.getAttribute('aria-label') || el.getAttribute('title')); + if (a) s += `[${String(a).slice(0, 24)}]`; + return s; + } catch { return '?'; } +} try { if (window.PerformanceObserver) { new window.PerformanceObserver((list) => { - for (const e of list.getEntries()) { _rLongTasks++; if (e.duration > _rLongTaskMax) _rLongTaskMax = e.duration; } + for (const e of list.getEntries()) { + _rLongTasks++; + if (e.duration > _rLongTaskMax) _rLongTaskMax = e.duration; + if (e.duration >= 100 && window.api && window.api.debugLog) window.api.debugLog(`renderer-longtask dur=${Math.round(e.duration)}ms`); + } }).observe({ entryTypes: ['longtask'] }); } } catch {} +try { + if (window.PerformanceObserver) { + new window.PerformanceObserver((list) => { + for (const e of list.getEntries()) { + const proc = Math.round((e.processingEnd || 0) - (e.processingStart || 0)); + if (window.api && window.api.debugLog) window.api.debugLog(`renderer-interaction ${e.name} dur=${Math.round(e.duration)}ms proc=${proc}ms target=${_rElLabel(e.target)}`); + } + }).observe({ type: 'event', durationThreshold: 50, buffered: true }); + } +} catch {} function _rFrameTick(ts) { if (_rFrameLast) { const d = ts - _rFrameLast; _rFrameCount++; if (d > _rFrameWorst) _rFrameWorst = d; if (d > 33) _rFrameJank++; } _rFrameLast = ts; diff --git a/tasks/todo.md b/tasks/todo.md index 8e85c7c..b47bce3 100644 --- a/tasks/todo.md +++ b/tasks/todo.md @@ -1,3 +1,19 @@ +# v3.3.100 — close the LAST measurement gap: renderer interaction timing (switches/clicks) + +User asked "haben wir wirklich ALLES gemessen, auch switches/wechsel?". Audit: main-side was already +fully covered (IPC wrapper ≥50ms on every handler, main-longtask >100ms with lastIpc, config instrument); +account switchAccount is a trivial sync Map-set + the rotation work is async (can't block) → already covered. +The REAL gap was RENDERER-side: renderer-perf was upload-gated (idle clicks unmeasured) and only aggregate +(no per-interaction latency, no element attribution). Closed it (renderer/app.js, additive, self-silencing): +- Event Timing API observer (`type:'event', durationThreshold:50, buffered`) → `renderer-interaction + dur=Xms proc=Yms target=` for EVERY UI interaction ≥50ms (switch/sort-header/tab/button), always-on, + names the element (id/class/data-action/aria-label). The direct "click→reaction" latency. +- Idle renderer-longtask logging: any longtask ≥100ms logged immediately (`renderer-longtask dur=Xms`), + not just during uploads. +405 tests pass, clean boot. Now EVERY action — main or renderer, idle or under load — names itself if slow. + +--- + # v3.3.99 — THE KILL: 38.5MB config-thrash → history split out of the hot config + full instrumentation THE ROOT CAUSE (from v3.3.98's instrument, the real "bread"): electron-config.json was **38.5MB** and got