Fix chart label clipping, persist speed history across tabs, fix pause button

- Dynamic left padding based on measured label width (no more cut-off numbers)
- Speed history ref lifted to App so chart data survives tab switches
- Pause button uses optimistic UI update for instant visual feedback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe 2026-03-02 19:11:36 +01:00
parent ba673b9e53
commit e6c17393fb
2 changed files with 31 additions and 18 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "real-debrid-downloader", "name": "real-debrid-downloader",
"version": "1.5.0", "version": "1.5.1",
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)", "description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
"main": "build/main/main/main.js", "main": "build/main/main/main.js",
"author": "Sucukdeluxe", "author": "Sucukdeluxe",

View File

@ -136,12 +136,12 @@ interface BandwidthChartProps {
items: Record<string, DownloadItem>; items: Record<string, DownloadItem>;
running: boolean; running: boolean;
paused: boolean; paused: boolean;
speedHistoryRef: React.MutableRefObject<{ time: number; speed: number }[]>;
} }
const BandwidthChart = memo(function BandwidthChart({ items, running, paused }: BandwidthChartProps): ReactElement { const BandwidthChart = memo(function BandwidthChart({ items, running, paused, speedHistoryRef }: BandwidthChartProps): ReactElement {
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const speedHistoryRef = useRef<{ time: number; speed: number }[]>([]);
const lastUpdateRef = useRef<number>(0); const lastUpdateRef = useRef<number>(0);
const [, forceUpdate] = useState(0); const [, forceUpdate] = useState(0);
const animationFrameRef = useRef<number>(0); const animationFrameRef = useRef<number>(0);
@ -162,9 +162,6 @@ const BandwidthChart = memo(function BandwidthChart({ items, running, paused }:
canvas.width = width * dpr; canvas.width = width * dpr;
canvas.height = height * dpr; canvas.height = height * dpr;
ctx.scale(dpr, dpr); ctx.scale(dpr, dpr);
const padding = { top: 20, right: 20, bottom: 30, left: 60 };
const chartWidth = width - padding.left - padding.right;
const chartHeight = height - padding.top - padding.bottom;
ctx.clearRect(0, 0, width, height); ctx.clearRect(0, 0, width, height);
@ -174,16 +171,6 @@ const BandwidthChart = memo(function BandwidthChart({ items, running, paused }:
const accentColor = isDark ? "#38bdf8" : "#1168d9"; const accentColor = isDark ? "#38bdf8" : "#1168d9";
const fillColor = isDark ? "rgba(56, 189, 248, 0.15)" : "rgba(17, 104, 217, 0.15)"; const fillColor = isDark ? "rgba(56, 189, 248, 0.15)" : "rgba(17, 104, 217, 0.15)";
ctx.strokeStyle = gridColor;
ctx.lineWidth = 1;
for (let i = 0; i <= 5; i += 1) {
const y = padding.top + (chartHeight / 5) * i;
ctx.beginPath();
ctx.moveTo(padding.left, y);
ctx.lineTo(width - padding.right, y);
ctx.stroke();
}
const history = speedHistoryRef.current; const history = speedHistoryRef.current;
const now = Date.now(); const now = Date.now();
const maxTime = now; const maxTime = now;
@ -196,6 +183,28 @@ const BandwidthChart = memo(function BandwidthChart({ items, running, paused }:
maxSpeed = Math.max(maxSpeed, 1024 * 1024); maxSpeed = Math.max(maxSpeed, 1024 * 1024);
const niceMax = Math.pow(2, Math.ceil(Math.log2(maxSpeed))); const niceMax = Math.pow(2, Math.ceil(Math.log2(maxSpeed)));
// Measure widest label to set dynamic left padding
ctx.font = "11px 'Manrope', sans-serif";
let maxLabelWidth = 0;
for (let i = 0; i <= 5; i += 1) {
const speedVal = niceMax * (1 - i / 5);
const w = ctx.measureText(formatSpeedMbps(speedVal)).width;
if (w > maxLabelWidth) maxLabelWidth = w;
}
const padding = { top: 20, right: 20, bottom: 30, left: Math.ceil(maxLabelWidth) + 16 };
const chartWidth = width - padding.left - padding.right;
const chartHeight = height - padding.top - padding.bottom;
ctx.strokeStyle = gridColor;
ctx.lineWidth = 1;
for (let i = 0; i <= 5; i += 1) {
const y = padding.top + (chartHeight / 5) * i;
ctx.beginPath();
ctx.moveTo(padding.left, y);
ctx.lineTo(width - padding.right, y);
ctx.stroke();
}
ctx.fillStyle = textColor; ctx.fillStyle = textColor;
ctx.font = "11px 'Manrope', sans-serif"; ctx.font = "11px 'Manrope', sans-serif";
ctx.textAlign = "right"; ctx.textAlign = "right";
@ -1415,6 +1424,7 @@ export function App(): ReactElement {
setContextMenu({ x, y, packageId, itemId }); setContextMenu({ x, y, packageId, itemId });
}, []); }, []);
const speedHistoryRef = useRef<{ time: number; speed: number }[]>([]);
const dragSelectRef = useRef(false); const dragSelectRef = useRef(false);
const dragAnchorRef = useRef<string | null>(null); const dragAnchorRef = useRef<string | null>(null);
const dragDidMoveRef = useRef(false); const dragDidMoveRef = useRef(false);
@ -1903,7 +1913,10 @@ export function App(): ReactElement {
className={`ctrl-icon-btn ctrl-pause${snapshot.session.running && !snapshot.session.paused ? " active" : ""}${snapshot.session.paused ? " paused" : ""}`} className={`ctrl-icon-btn ctrl-pause${snapshot.session.running && !snapshot.session.paused ? " active" : ""}${snapshot.session.paused ? " paused" : ""}`}
title={snapshot.session.paused ? "Fortsetzen" : "Pause"} title={snapshot.session.paused ? "Fortsetzen" : "Pause"}
disabled={!snapshot.canPause} disabled={!snapshot.canPause}
onClick={() => { void window.rd.togglePause(); }} onClick={() => {
setSnapshot((prev) => ({ ...prev, session: { ...prev.session, paused: !prev.session.paused } }));
void window.rd.togglePause();
}}
> >
<svg viewBox="0 0 24 24" width="18" height="18"><rect x="5" y="3" width="4.5" height="18" rx="1" fill="currentColor" /><rect x="14.5" y="3" width="4.5" height="18" rx="1" fill="currentColor" /></svg> <svg viewBox="0 0 24 24" width="18" height="18"><rect x="5" y="3" width="4.5" height="18" rx="1" fill="currentColor" /><rect x="14.5" y="3" width="4.5" height="18" rx="1" fill="currentColor" /></svg>
</button> </button>
@ -2157,7 +2170,7 @@ export function App(): ReactElement {
<article className="card stats-chart-card"> <article className="card stats-chart-card">
<h3>Bandbreitenverlauf</h3> <h3>Bandbreitenverlauf</h3>
<BandwidthChart items={snapshot.session.items} running={snapshot.session.running} paused={snapshot.session.paused} /> <BandwidthChart items={snapshot.session.items} running={snapshot.session.running} paused={snapshot.session.paused} speedHistoryRef={speedHistoryRef} />
</article> </article>
<article className="card stats-provider-card"> <article className="card stats-provider-card">