From fe3cb36731102d1fc3c4d3431c1f60eec59ff230 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Mon, 17 Aug 2026 13:39:53 +0200 Subject: [PATCH] perf(ui): calm large-queue live updates Throttle visible download snapshots at 500 ms from 250 queue items, align speed visualizations to the same cadence, preserve small-queue responsiveness, and retain the stronger protection for very large queues. --- CHANGELOG.md | 8 ++++++++ package-lock.json | 4 ++-- package.json | 2 +- src/main/download-manager.ts | 30 +++++++++++++----------------- src/renderer/App.tsx | 6 +++--- tests/app-shell.test.tsx | 4 ++++ tests/statistics-view.test.tsx | 2 +- 7 files changed, 32 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 722f8f3..d586ef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to Multi-Debrid Downloader are documented in this file. ## [Unreleased] +## [2.0.44] - 2026-08-17 + +### Live updates + +- Reduced visible download, remaining-volume, speed, ETA, and availability refresh churn for queues with 250 or more items by using a calm 500 ms cadence. +- Updated live speed sparklines and the bandwidth chart to redraw every 500 ms instead of every 250 ms. +- Kept small queues responsive and retained the stronger 700 ms protection for queues with 1,500 or more items. + ## [2.0.43] - 2026-08-16 ### Product identity diff --git a/package-lock.json b/package-lock.json index 0b59f8c..a2154de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "multi-debrid-downloader", - "version": "2.0.43", + "version": "2.0.44", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "multi-debrid-downloader", - "version": "2.0.43", + "version": "2.0.44", "license": "MIT", "dependencies": { "adm-zip": "0.6.0", diff --git a/package.json b/package.json index ef81037..81388d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "multi-debrid-downloader", - "version": "2.0.43", + "version": "2.0.44", "description": "Desktop downloader", "main": "build/main/main/main.js", "author": "Sucukdeluxe", diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index bb76e58..23f8e69 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -6547,15 +6547,13 @@ export class DownloadManager extends EventEmitter { if (this.stateEmitTimer) { return; } - const itemCount = this.itemCount; - const emitDelay = this.session.running - ? itemCount >= 1500 - ? 700 - : itemCount >= 700 - ? 500 - : itemCount >= 250 - ? 300 - : 150 + const itemCount = this.itemCount; + const emitDelay = this.session.running + ? itemCount >= 1500 + ? 700 + : itemCount >= 250 + ? 500 + : 150 : 200; this.stateEmitTimer = setTimeout(() => { this.stateEmitTimer = null; @@ -10566,14 +10564,12 @@ export class DownloadManager extends EventEmitter { let windowStarted = nowMs(); let lastPackageLogAt = 0; let lastLoggedPercent = -1; - const itemCount = this.itemCount; - const uiUpdateIntervalMs = itemCount >= 1500 - ? 500 - : itemCount >= 700 - ? 350 - : itemCount >= 250 - ? 220 - : 120; + const itemCount = this.itemCount; + const uiUpdateIntervalMs = itemCount >= 1500 + ? 700 + : itemCount >= 250 + ? 500 + : 120; let lastUiEmitAt = 0; const stallTimeoutMs = getDownloadStallTimeoutMs(); const drainTimeoutMs = Math.max(30000, Math.min(300000, stallTimeoutMs > 0 ? stallTimeoutMs * 12 : 120000)); diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 854e743..cc1e17a 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -880,7 +880,7 @@ const historyRetentionLabels: Record= 700 ? 0 : itemCount >= 250 ? 50 : 100; + let delay = itemCount >= 250 ? 0 : 100; if (!running) delay = Math.min(delay, 200); if (activeTab !== "downloads") delay = Math.max(delay, 800); return delay; @@ -1272,7 +1272,7 @@ const BandwidthChart = memo(function BandwidthChart({ running, paused, speedHist const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; const interval = setInterval(() => { drawChart(); - }, reducedMotion ? 1000 : 250); + }, reducedMotion ? 1000 : 500); return () => clearInterval(interval); }, [drawChart, running, paused]); @@ -1374,7 +1374,7 @@ const DownloadSpeedSparkline = memo(function DownloadSpeedSparkline({ speedBps, draw(); }; - const id = window.setInterval(tick, 250); + const id = window.setInterval(tick, 500); return () => window.clearInterval(id); }, []); diff --git a/tests/app-shell.test.tsx b/tests/app-shell.test.tsx index 7239ad4..1aa99b7 100644 --- a/tests/app-shell.test.tsx +++ b/tests/app-shell.test.tsx @@ -50,6 +50,10 @@ describe("desktop shell", () => { expect(getSnapshotRenderDelay(2_470, true, "statistics")).toBe(800); }); + it("renders medium download queues immediately after their calm half-second snapshot", () => { + expect(getSnapshotRenderDelay(661, true, "downloads")).toBe(0); + }); + it("keeps application menus mounted for animated opening and closing", () => { const source = readFileSync(new URL("../src/renderer/App.tsx", import.meta.url), "utf8"); const css = readFileSync(new URL("../src/renderer/styles.css", import.meta.url), "utf8"); diff --git a/tests/statistics-view.test.tsx b/tests/statistics-view.test.tsx index 16faf0d..6b864ca 100644 --- a/tests/statistics-view.test.tsx +++ b/tests/statistics-view.test.tsx @@ -507,7 +507,7 @@ describe("bandwidth chart palette", () => { expect(chartBlock).toContain('role="img"'); expect(chartBlock).toContain('aria-label="Bandbreitenverlauf der letzten 60 Sekunden"'); expect(chartBlock).toContain('window.matchMedia("(prefers-reduced-motion: reduce)")'); - expect(chartBlock).toContain("reducedMotion ? 1000 : 250"); + expect(chartBlock).toContain("reducedMotion ? 1000 : 500"); }); it("asks for confirmation before deleting all saved download statistics", () => {