feat(uploads): Fortschritt in MB, Tempo und Restzeit; Abbruchkreuz rot

Das Kreuz faerbt sich beim Darueberfahren rot -- aber nur, solange der
Upload laeuft. Dasselbe Zeichen tut zwei Dinge: eine Meldung wegraeumen
ist folgenlos, einen laufenden Vorgang fallen zu lassen nicht.

Unter dem Balken steht jetzt "4.2 MB / 18.0 MB · 1.8 MB/s · 8 s left".
Die Zahlen kamen schon immer vom XMLHttpRequest, sie wurden nur
weggeworfen. Neu gerechnet wird nur das Tempo -- der Browser meldet
Bytes, nicht deren Ableitung.

Zwei Daempfungen, aus zwei Gruenden:

* Das Tempo aus einem einzelnen Abstand springt zwischen dem Doppelten
  und der Haelfte. Gleitender Mittelwert (0,7/0,3) ueber Messpunkte von
  mindestens 0,2 s.
* Die Anzeige wird hoechstens alle 150 ms nachgezogen. Der Browser meldet
  im 50-ms-Takt; bei drei Dateien waeren das sechzig Neuaufbauten der
  Liste je Sekunde fuer eine Zahl, die niemand so schnell liest.

Jeder Teil erscheint erst, wenn er stimmt: Groesse sofort, Tempo nach dem
zweiten Messpunkt, Restzeit erst mit beidem. Eine Schaetzung aus dem
Nichts waere schlimmer als keine -- man richtet sich danach.

formatDuration in src/format.ts, 5 Tests (37 gesamt).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-09-09 08:46:28 +02:00
1 parent 511213b40d
commit e9f69f7d7f
4 files changed
+175 -14

No files matched your search

+22
View File
@@ -21,3 +21,25 @@ export function formatBytes(n: number): string {
if (n < 1024 ** 3) return `${(n / 1024 ** 2).toFixed(1)} MB`
return `${(n / 1024 ** 3).toFixed(1)} GB`
}
/**
* Eine Dauer, wie man sie beim Warten liest.
*
* Grob genug, dass die Zahl nicht zappelt, genau genug, dass sie etwas sagt:
* unter einer Minute in Sekunden, darüber als `3:07 min`. Sekunden bis in die
* zehnte Minute auszuschreiben hieße eine Zahl, die sich jede Sekunde ändert
* und trotzdem niemandem hilft.
*
* Leer bei allem, was keine Dauer ist – unendlich, negativ, keine Zahl. Der
* Aufrufer zeigt dann gar nichts an, statt „NaN s" zu schreiben.
*/
export function formatDuration(seconds: number): string {
if (!Number.isFinite(seconds) || seconds < 0) return ''
// Unter einer Sekunde bleibt "1 s" stehen: "0 s" sähe aus wie fertig,
// während der Balken noch läuft.
if (seconds < 60) return `${Math.max(1, Math.round(seconds))} s`
const total = Math.round(seconds)
const m = Math.floor(total / 60)
if (m < 60) return `${m}:${String(total % 60).padStart(2, '0')} min`
return `${Math.floor(m / 60)}:${String(m % 60).padStart(2, '0')} h`
}