diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx
index a958c1f..d023cac 100644
--- a/src/renderer/App.tsx
+++ b/src/renderer/App.tsx
@@ -3790,6 +3790,52 @@ export function App(): ReactElement {
: 0;
const liveSessionRuntimeMs = Math.max(0, (snapshot.stats.sessionRuntimeMs || 0) + runtimeOffsetMs);
const liveTotalRuntimeMs = Math.max(0, (snapshot.stats.totalRuntimeMs || 0) + runtimeOffsetMs);
+ const etaSeparatorIndex = snapshot.etaText.includes(": ") ? snapshot.etaText.indexOf(": ") : -1;
+ const etaValue = etaSeparatorIndex >= 0
+ ? snapshot.etaText.slice(etaSeparatorIndex + 2)
+ : "--";
+ const resetFailedDownloads = (): void => {
+ if (itemStatusCounts.failed === 0) return;
+ const failedIds = Object.values(snapshot.session.items)
+ .filter((it) => it.status === "failed")
+ .map((it) => it.id);
+ void window.rd.resetItems(failedIds).catch(() => {});
+ };
+ const statsSections = [
+ {
+ key: "live",
+ title: "Aktuell",
+ items: [
+ { key: "speed", eyebrow: "Live", label: "Geschwindigkeit", value: snapshot.speedText.replace("Geschwindigkeit: ", "") },
+ { key: "eta", eyebrow: "Live", label: "Restzeit", value: etaValue, compactValue: true },
+ { key: "active", eyebrow: "Queue", label: "Aktive Downloads", value: String(itemStatusCounts.downloading) },
+ { key: "queued", eyebrow: "Queue", label: "In Warteschlange", value: String(itemStatusCounts.queued) },
+ {
+ key: "failed",
+ eyebrow: "Status",
+ label: "Fehlerhaft",
+ value: String(itemStatusCounts.failed),
+ danger: itemStatusCounts.failed > 0,
+ clickable: itemStatusCounts.failed > 0,
+ title: itemStatusCounts.failed > 0 ? "Klicken zum Zurücksetzen aller fehlerhaften Downloads" : undefined,
+ onClick: resetFailedDownloads
+ },
+ { key: "packages", eyebrow: "Queue", label: "Pakete", value: String(snapshot.stats.totalPackages) }
+ ]
+ },
+ {
+ key: "summary",
+ title: "Bilanz",
+ items: [
+ { key: "downloaded-session", eyebrow: "Session", label: "Heruntergeladen", value: humanSize(snapshot.stats.totalDownloaded) },
+ { key: "downloaded-total", eyebrow: "Gesamt", label: "Heruntergeladen", value: humanSize(snapshot.stats.totalDownloadedAllTime) },
+ { key: "runtime-session", eyebrow: "Session", label: "Laufzeit", value: formatRuntimeDuration(liveSessionRuntimeMs), compactValue: true },
+ { key: "runtime-total", eyebrow: "Gesamt", label: "Laufzeit", value: formatRuntimeDuration(liveTotalRuntimeMs), compactValue: true },
+ { key: "files-session", eyebrow: "Session", label: "Fertige Dateien", value: String(snapshot.stats.totalFilesSession) },
+ { key: "files-total", eyebrow: "Gesamt", label: "Fertige Dateien", value: String(snapshot.stats.totalFilesAllTime) }
+ ]
+ }
+ ] as const;
return (
Heruntergeladen zurücksetzen
-
-
- Aktuelle Geschwindigkeit
- {snapshot.speedText.replace("Geschwindigkeit: ", "")}
-
-
- Heruntergeladen (Session)
- {humanSize(snapshot.stats.totalDownloaded)}
-
-
- Heruntergeladen (Gesamt)
- {humanSize(snapshot.stats.totalDownloadedAllTime)}
-
+
+ {statsSections.map((section) => (
+
+ {section.title}
+
+ {section.items.map((item) => item.onClick ? (
+
+ ) : (
+
+
+ {item.eyebrow}
+ {item.label}
+
+ {item.value}
+
+ ))}
+
+
+ ))}
+ {false && (<>
Laufzeit (Session)
{formatRuntimeDuration(liveSessionRuntimeMs)}
@@ -4493,6 +4558,7 @@ export function App(): ReactElement {
{snapshot.etaText.includes(": ") ? snapshot.etaText.slice(0, snapshot.etaText.indexOf(": ")) : snapshot.etaText}
{snapshot.etaText.includes(": ") ? snapshot.etaText.slice(snapshot.etaText.indexOf(": ") + 2) : "--"}
+ >)}
@@ -6120,4 +6186,3 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, stripe
}
return true;
});
-
diff --git a/src/renderer/styles.css b/src/renderer/styles.css
index 8c1f3d0..34c916e 100644
--- a/src/renderer/styles.css
+++ b/src/renderer/styles.css
@@ -2501,39 +2501,97 @@ td {
margin-top: 10px;
}
+.stats-sections {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ margin-top: 14px;
+}
+
+.stats-section {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.stats-section-title {
+ color: var(--muted);
+ font-size: 11px;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+}
+
.stats-grid {
display: grid;
- grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
+ grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 12px;
- margin-top: 8px;
}
.stat-item {
display: flex;
flex-direction: column;
- gap: 4px;
- padding: 10px 12px;
+ justify-content: space-between;
+ gap: 12px;
+ min-height: 96px;
+ min-width: 0;
+ padding: 12px 14px;
background: var(--field);
border-radius: 10px;
border: 1px solid var(--border);
+ text-align: left;
}
+
+.stat-button {
+ width: 100%;
+ font: inherit;
+ color: inherit;
+ appearance: none;
+ cursor: default;
+}
+
+.stat-top {
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+ min-width: 0;
+}
+
+.stat-eyebrow {
+ color: var(--muted);
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+}
+
.stat-item.stat-item-clickable {
cursor: pointer;
- transition: border-color 0.15s;
+ transition: border-color 0.15s, transform 0.15s ease;
}
.stat-item.stat-item-clickable:hover {
border-color: var(--danger, #e05555);
+ transform: translateY(-1px);
}
.stat-label {
- color: var(--muted);
- font-size: 12px;
+ color: var(--text);
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 1.25;
}
.stat-value {
- font-size: 18px;
+ font-size: clamp(22px, 1.35vw, 30px);
font-weight: 700;
font-variant-numeric: tabular-nums;
+ line-height: 1.1;
+ overflow-wrap: anywhere;
+}
+
+.stat-value.stat-value-compact {
+ font-size: clamp(18px, 1.05vw, 24px);
+ line-height: 1.2;
}
.stat-value.danger {
@@ -2629,6 +2687,12 @@ td {
font-size: 13px;
}
+@media (max-width: 1680px) {
+ .stats-grid {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ }
+}
+
@media (max-width: 1100px) {
.statistics-view {
grid-template-columns: 1fr;
@@ -2640,7 +2704,23 @@ td {
}
.stats-grid {
- grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 760px) {
+ .stats-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .stat-item {
+ min-height: 88px;
+ }
+}
+
+@media (max-width: 520px) {
+ .stats-grid {
+ grid-template-columns: 1fr;
}
}