release: restore v2.1.19 baseline for v2.1.24
CI / verify (push) Has been cancelled

Restore the v2.1.19 application baseline and retain only the focused import preflight summary with duplicate, unavailable, destination, job, and size-limit visibility.
This commit is contained in:
Sucukdeluxe
2026-08-17 04:25:22 +02:00
parent 9a213a7395
commit d7c9f287e4
87 changed files with 2185 additions and 16125 deletions
+603 -1321
View File
File diff suppressed because it is too large Load Diff
+4 -12
View File
@@ -79,20 +79,12 @@
e.preventDefault();
e.stopPropagation();
target.classList.remove('drag-over');
const files = [];
const paths = [];
for (const file of e.dataTransfer.files) {
let filePath = '';
try { filePath = window.dropTargetApi.getPathForFile(file); } catch {}
if (!filePath) continue;
files.push({
path: filePath,
name: file.name || '',
size: Number.isFinite(file.size) ? file.size : 0,
isDirectory: file.type === '' && file.size === 0
});
if (file.path) paths.push(file.path);
}
if (files.length > 0) {
window.dropTargetApi.sendFiles(files);
if (paths.length > 0) {
window.dropTargetApi.sendFiles(paths);
}
});
</script>
+15 -155
View File
@@ -24,7 +24,6 @@
['Maximale Größe (MB)', 'Maximum size (MB)'],
['Bildschirm und Eingabesteuerung bleiben gesperrt.', 'Screen and input control remain locked.'],
['Bindet nur an', 'Binds only to'],
['. Fernzugriff nur über einen Tunnel (z.B. Tailscale/SSH).', '. Remote access is only available through a tunnel (for example, Tailscale/SSH).'],
['Bindet an', 'Binds to'],
['. Nur IPs/CIDRs aus der Allowlist dürfen verbinden (Loopback immer) — zusätzlich zum Token. Über Tailscale: trage deinen Tailnet-Bereich ein (z.B.', '. Only IPs/CIDRs from the allowlist may connect (loopback is always allowed), in addition to the token. For Tailscale, enter your tailnet range, e.g.'],
[') und die Tailscale-IP/MagicDNS oben als Code-Adresse. Transport ist plaintext über den Tunnel — Tailscale/WireGuard verschlüsselt.', ') and enter the Tailscale IP or MagicDNS name above as the code address. Transport is plaintext through the tunnel; Tailscale/WireGuard provides encryption.'],
@@ -37,58 +36,20 @@
['Fensterverhalten, Drop-Target und Programmupdates.', 'Window behavior, drop target, and application updates.'],
['Gesamt:', 'Total:'],
['Globales Speed-Limit', 'Global speed limit'],
['Dateinamenfilter', 'Filename filter'],
['Dateinamen beim Hinzufügen filtern', 'Filter filenames when adding files'],
['Prüft neue Dateien aus Auswahl, Ordnern, Drag-and-drop und Ordnerüberwachung, bevor sie in die Upload-Liste gelangen.', 'Checks new files from selections, folders, drag and drop, and folder monitoring before they enter the upload list.'],
['Treffer', 'Matches'],
['Wenn passend', 'When matched'],
['Bedingungen', 'Conditions'],
['nur hinzufügen', 'add only'],
['nicht hinzufügen', 'do not add'],
['Dateien hinzufügen', 'Add files'],
['Dateien nicht hinzufügen', 'Do not add files'],
['alle müssen passen', 'all must match'],
['mindestens eine muss passen', 'at least one must match'],
['Dateinamen-Bedingung', 'Filename condition'],
['Vergleich', 'Comparison'],
['enthält', 'contains'],
['enthält nicht', 'does not contain'],
['Dateiname filtern', 'Filter file name'],
['z. B. 720p', 'e.g. 720p'],
['Bedingung entfernen', 'Remove condition'],
['+ Bedingung', '+ Condition'],
['Groß- und Kleinschreibung werden ignoriert. Leere Bedingungen werden nicht gespeichert.', 'Matching ignores letter case. Empty conditions are not saved.'],
['Hauptbereiche', 'Main sections'],
['Hinweis', 'Notice'],
['Hoster-Limits automatisch hochskalieren', 'Automatically scale host limits'],
['Hoster', 'Host'],
['Versuch', 'Attempt'],
['Batch abgeschlossen', 'Batch complete'],
['Der Upload-Batch wurde abgeschlossen.', 'The upload batch has completed.'],
['Mit Problemen', 'Completed with issues'],
['Übertragung', 'Transfer'],
['Dauer', 'Duration'],
['Erfolgreich übertragen', 'Successfully transferred'],
['Durchschnitt', 'Average'],
['Vollständig erfolgreich', 'Fully successful'],
['Teilweise erfolgreich', 'Partially successful'],
['Auftrag', 'Job'],
['Aufträge', 'Jobs'],
['Angefordert', 'Requested'],
['Gelöscht', 'Deleted'],
['Blockiert', 'Blocked'],
['Hosterübersicht', 'Host overview'],
['Übertragen', 'Transferred'],
['Fehlerbeispiele', 'Error examples'],
['Fehler-CSV exportieren', 'Export error CSV'],
['Temporärer Hosterfehler', 'Temporary host error'],
['Datei abgelehnt', 'File rejected'],
['Account-Fehler', 'Account error'],
['Remote-Abschluss unklar', 'Remote completion uncertain'],
['1 weiterer Fehler', '1 more error'],
['JSON-Bericht exportiert', 'JSON report exported'],
['Fehler-CSV exportiert', 'Error CSV exported'],
['Batch-Bericht konnte nicht exportiert werden.', 'The batch report could not be exported.'],
['Import-Vorabprüfung', 'Import preflight'],
['Kandidaten', 'Candidates'],
['Bereits vorhanden / dupliziert', 'Already present / duplicated'],
['Fehlend / unlesbar / leer', 'Missing / unreadable / empty'],
['Akzeptierte Dateien', 'Accepted files'],
['Ausgewählte Ziele', 'Selected destinations'],
['Entstehende Jobs', 'Resulting jobs'],
['Durch konfigurierte Größenlimits entfallene Jobs', 'Jobs omitted by configured size limits'],
['Vorabprüfung abgeschlossen. Wähle jetzt die Hoster für den Upload.', 'Preflight complete. Choose upload destinations.'],
['Vorabprüfung fehlgeschlagen.', 'Preflight failed.'],
['Importieren', 'Import'],
['In Zwischenablage', 'To clipboard'],
['In diesem Lauf hochgeladen:', 'Uploaded during this run:'],
@@ -202,20 +163,6 @@
['Hoster-Zugangsdaten verwalten und prüfen', 'Manage and verify host account credentials'],
['Accounts prüfen', 'Check accounts'],
['Account hinzufügen', 'Add account'],
['Hoster-Gesundheit', 'Host health'],
['Hoster-Gesundheit aus lokalem Verlauf und aktuellem Accountstatus', 'Host health from local history and current account status'],
['Lokale Werte aus höchstens 50 Batches. Der effektive historische Durchsatz kann Wartezeiten und Wiederholungen enthalten.', 'Local values from up to 50 batches. Effective historical throughput can include waits and retries.'],
['Stichprobe', 'Sample'],
['Erfolg / Fehler / Übersprungen', 'Success / Failed / Skipped'],
['Erfolgsrate', 'Success rate'],
['Effektiver historischer Durchsatz', 'Effective historical throughput'],
['Letzter Erfolg', 'Last success'],
['Fehler (7 Tage)', 'Failed (7 days)'],
['Account-Probleme', 'Account issues'],
['Noch keine Hoster-Daten.', 'No host data yet.'],
['Verlauf nicht verfügbar.', 'History unavailable.'],
['Prüfung läuft', 'Check in progress'],
['Nie', 'Never'],
['Noch keine Hoster', 'No hosts yet'],
['Füge deinen ersten Hoster-Account hinzu. Die Zugangsdaten werden vor dem Speichern geprüft.', 'Add your first host account. Credentials are verified before saving.'],
['Alle ausklappen', 'Expand all'],
@@ -277,45 +224,18 @@
['Hoster-Einstellungen', 'Host settings'],
['Upload-Einstellungen', 'Upload settings'],
['Erfolgreiche Links in fileuploader.log.', 'Successful links in fileuploader.log.'],
['Verteilt Dateien abwechselnd auf alle aktiven Accounts dieses Hosters. Bei nur einem Account hat die Option keinen Effekt.', 'Distributes files alternately across all active accounts for this host. The option has no effect with only one account.'],
['Verteilt die Dateien reihum auf alle aktiven Accounts dieses Hosters (Datei 1 → Account 1, Datei 2 → Account 2 …). Hält z. B. byse-Accounts aktiv. Nur ein Account = kein Effekt.', 'Distributes files across all active accounts for this host in round-robin order (file 1 → account 1, file 2 → account 2, and so on). Keeps accounts such as byse active. Has no effect with only one account.'],
['Größen-Limit merken', 'Remember size limit'],
['Überspringt nach zwei verdächtigen Ablehnungen auf einem Account größere Dateien dort vorab ("Bekanntes Größen-Limit"). Abschalten = jede Datei wird immer wirklich versucht.', 'After two suspicious rejections on an account, larger files are skipped there in advance ("Known size limit"). When disabled, every file is always attempted.'],
['Einstellungen einzelner Hoster', 'Settings for individual hosts'],
['wie Wiederholungen, Geschwindigkeit, Parallelität, Dateigröße und Logging findest du im', 'such as retries, speed, parallelism, file size, and logging are available in'],
['Accounts-Tab.', 'the Accounts tab.'],
['Wiederholungen und überwachte Ordner für unbeaufsichtigte Uploads.', 'Retries and watched folders for unattended uploads.'],
['Wiederholungen, Upload-Zeitfenster und überwachte Ordner für unbeaufsichtigte Uploads.', 'Retries, upload schedules, and watched folders for unattended uploads.'],
['Unbeaufsichtigter Betrieb', 'Unattended operation'],
['Automatische Wiederholungsrunden', 'Automatic retry rounds'],
['0 = aus. Nach Batch-Ende werden transiente Fehler (Netzwerk, Hoster-Flake) automatisch bis zu N Runden neu versucht.', '0 = off. After a batch ends, transient errors (network or host issues) are retried automatically for up to N rounds.'],
['Wartezeit zwischen Runden', 'Delay between rounds'],
['Minuten · jede weitere Runde wartet entsprechend länger', 'Minutes · each additional round waits proportionally longer'],
['Upload-Zeitfenster', 'Upload schedule'],
['Neue Uploads nur im Zeitfenster starten', 'Start new uploads only during the schedule'],
['Laufende Uploads dürfen fertig werden. Wartende Uploads starten automatisch bei der nächsten Öffnung.', 'Active uploads may finish. Waiting uploads start automatically when the schedule opens again.'],
['Erlaubte Wochentage', 'Allowed weekdays'],
['Mo', 'Mon'],
['Di', 'Tue'],
['Mi', 'Wed'],
['Do', 'Thu'],
['Fr', 'Fri'],
['Sa', 'Sat'],
['So', 'Sun'],
['Start', 'Start'],
['Ende', 'End'],
['Lokale Systemzeit · Zeitfenster über Mitternacht werden unterstützt', 'Local system time · overnight schedules are supported'],
['Deaktiviert. Neue Uploads starten sofort.', 'Disabled. New uploads start immediately.'],
['Ungültig. Wähle mindestens einen Wochentag aus.', 'Invalid. Select at least one weekday.'],
['Ungültig. Start und Ende müssen unterschiedlich sein.', 'Invalid. Start and end must be different.'],
['Ungültig. Prüfe Start- und Endzeit.', 'Invalid. Check the start and end time.'],
['Geöffnet. Neue Uploads dürfen starten.', 'Open. New uploads may start.'],
['Geschlossen. Nächster erlaubter Start:', 'Closed. Next allowed start:'],
['Geöffnet', 'Open'],
['Geschlossen', 'Closed'],
['Ungültig', 'Invalid'],
['Wähle mindestens einen Wochentag für das Upload-Zeitfenster aus.', 'Select at least one weekday for the upload schedule.'],
['Start und Ende des Upload-Zeitfensters müssen unterschiedlich sein.', 'The upload schedule start and end must be different.'],
['Prüfe Start und Ende des Upload-Zeitfensters.', 'Check the upload schedule start and end.'],
['Ordnerüberwachung', 'Folder monitoring'],
['Inaktiv', 'Inactive'],
['Ordnerpfad', 'Folder path'],
@@ -406,18 +326,6 @@
['Ausgewählte starten', 'Start selected'],
['Upload-Ziele auswählen', 'Choose upload destinations'],
['Dateien wurden hinzugefügt. Wähle jetzt die Hoster für den Upload.', 'Files were added. Now choose the hosts for the upload.'],
['Vorabprüfung abgeschlossen. Wähle jetzt die Hoster für den Upload.', 'Preflight complete. Now choose the hosts for the upload.'],
['Import-Vorabprüfung', 'Import preflight'],
['Kandidaten', 'Candidates'],
['Bereits vorhanden / dupliziert', 'Already present / duplicated'],
['Durch Dateinamenfilter ausgeschlossen', 'Excluded by filename filter'],
['Fehlend / unlesbar / leer', 'Missing / unreadable / empty'],
['Akzeptierte Dateien', 'Accepted files'],
['Ausgewählte Ziele', 'Selected destinations'],
['Entstehende Jobs', 'Resulting jobs'],
['Durch konfigurierte Größenlimits entfallene Jobs', 'Jobs omitted by configured size limits'],
['Keine Dateien wurden akzeptiert.', 'No files were accepted.'],
['Vorabprüfung fehlgeschlagen.', 'Preflight failed.'],
['Alle', 'All'],
['Keine', 'None'],
['Keine Hoster mit Zugangsdaten vorhanden. Bitte zuerst in den Accounts einen Login oder API-Key hinterlegen.', 'No hosts with credentials are available. Add a login or API key under Accounts first.'],
@@ -517,7 +425,7 @@
['Keine Antwort vom Hoster erhalten', 'No response received from the host'],
['Keine Einträge im Log gefunden', 'No log entries found'],
['Keine Einträge zum Exportieren.', 'No entries to export.'],
['Keine Jobs hinzugefügt', 'No jobs added'],
['Keine Jobs hinzugefuegt', 'No jobs added'],
['Keine Log-Einträge für diesen Job (entweder noch nichts passiert oder aus vorherigem Batch und schon geräumt).', 'No log entries for this job. Nothing has happened yet, or entries from a previous batch were already cleared.'],
['Keine passenden Jobs für Retry gefunden.', 'No matching jobs found for retry.'],
['Keine startbaren Jobs ausgewählt (alle laufen schon oder sind fertig).', 'No startable jobs selected because all are already running or completed.'],
@@ -573,13 +481,7 @@
['Ein Upload wird bereits ausgeführt oder abgeschlossen', 'An upload is already running or completed'],
['Kein gültiger Account für diesen Hoster', 'No valid account is available for this host'],
['Keine gültigen Zugangsdaten für die gewählten Hoster.', 'No valid credentials are available for the selected hosts.'],
['Upload-Ergebnis für diesen Titel ist wegen eines möglichen Remote-Commits unsicher', 'The upload result for this title is uncertain because of a possible remote commit'],
['Gemischte Upload-Anmeldungen für denselben Remote-Titel wurden sicher blockiert', 'Mixed upload authentication profiles for the same remote title were safely blocked'],
['Doodstream Upload lieferte eine bereits zugeordnete Remote-Identität', 'Doodstream upload returned an already claimed remote identity'],
['Unbekannter Fehler', 'Unknown error'],
['Zugriff verweigert', 'Access denied'],
['Datei beschädigt', 'File is damaged'],
['Konfiguration fehlt', 'Configuration is missing'],
['Kein Log-Pfad gefunden', 'No log path was found'],
['Ungültige URL (muss mit http(s):// beginnen)', 'Invalid URL (must start with http(s)://)'],
['Backup-Datei ist zu groß oder ungültig', 'The backup file is too large or invalid'],
@@ -657,10 +559,6 @@
['Backup importiert', 'Backup imported'],
['Import fehlgeschlagen', 'Import failed'],
['Upload-Start fehlgeschlagen', 'Failed to start upload'],
['Initialisierung fehlgeschlagen', 'Initialization failed'],
['Import übernommen. Warteschlange konnte nicht vollständig gespeichert werden', 'Import applied. The queue could not be saved completely'],
['Jobs konnten nicht hinzugefügt werden', 'Jobs could not be added'],
['Test fehlgeschlagen', 'Test failed'],
['erneut versuchbar', 'retryable'],
['manuell', 'manual'],
['Abgebrochen.', 'Canceled.'],
@@ -690,18 +588,9 @@
['Update wird installiert. Die Anwendung startet neu…', 'Installing update. The application is restarting…'],
['Updateprüfung fehlgeschlagen', 'Update check failed'],
['Upload läuft...', 'Uploading...'],
['Upload konnte nicht gestartet werden', 'The upload could not be started'],
['Upload wurde abgebrochen', 'The upload was canceled'],
['Upload-Log', 'Upload log'],
['Upload-Status', 'Upload status'],
['Uploadstatus konnte nicht bestätigt werden', 'The upload status could not be confirmed'],
['Abgebrochen; Remote-Status konnte nicht bestätigt werden', 'Cancelled; the remote upload status could not be confirmed'],
['Der Upload könnte beim Hoster bereits abgeschlossen sein. Ein erneuter Upload kann ein Duplikat erzeugen.', 'The upload may already be complete on the host. Uploading it again can create a duplicate.'],
['Trotzdem erneut hochladen', 'Upload again anyway'],
['Upload-Übersicht', 'Upload overview'],
['Upload-Wiederherstellung konnte nicht gespeichert werden', 'Upload recovery could not be saved'],
['Sitzungsbericht konnte nicht exportiert werden.', 'The session report could not be exported.'],
['Warteschlange konnte vor dem Upload-Start nicht gespeichert werden', 'The queue could not be saved before starting the upload'],
['Verlauf als CSV exportieren?\n\nOK = CSV\nAbbrechen = JSON', 'Export history as CSV?\n\nOK = CSV\nCancel = JSON'],
['Verlauf wirklich löschen?', 'Are you sure you want to delete the history?'],
['Verschlüssele und speichere Einstellungen…', 'Encrypting and saving settings…'],
@@ -750,27 +639,15 @@
const core = text.slice(leading.length, text.length - trailing.length);
const exact = target === 'en' ? deToEn.get(core) : enToDe.get(core);
if (exact) return `${leading}${exact}${trailing}`;
const translateErrorDetail = (detail) => {
const translated = target === 'en' ? deToEn.get(detail) : enToDe.get(detail);
return translated || (target === 'en' ? 'Unknown error' : 'Unbekannter Fehler');
};
const patterns = target === 'en'
? [
[/^Update v(.+) verfügbar$/, 'Update v$1 available'],
[/^1 unterbrochener Upload kann fortgesetzt werden\.$/, '1 interrupted upload can be resumed.'],
[/^(\d+) unterbrochene Uploads können fortgesetzt werden\.$/, '$1 interrupted uploads can be resumed.'],
[/^Wiederhergestellte Warteschlange startet in (.+) s \((.+) Jobs\)\.$/, 'Restored queue starts in $1 s ($2 jobs).'],
[/^(\d+) hinzugefügt$/, '$1 added'],
[/^(\d+) bereits im Batch$/, '$1 already in the batch'],
[/^(\d+) ohne gültigen Account$/, '$1 without a valid account'],
[/^(\d+) von (\d+) Dateien werden hinzugefügt\. (\d+) durch den Dateinamenfilter ausgeschlossen\.$/, '$1 of $2 files will be added. $3 excluded by the filename filter.'],
[/^Sitzungsbericht mit 1 Upload exportiert$/, 'Session report with 1 upload exported'],
[/^Sitzungsbericht mit (\d+) Uploads exportiert$/, 'Session report with $1 uploads exported'],
[/^Login ok, Upload-Form bereit \(Dateifeld: (.+)\)$/, 'Login successful, upload form ready (file field: $1)'],
[/^Klartext-Backup ist kein gültiges JSON: (.+)$/, 'Plain JSON backup is not valid JSON: $1'],
[/^Export fehlgeschlagen: (.+)$/, (_, detail) => `Export failed: ${translateErrorDetail(detail)}`],
[/^Import fehlgeschlagen: (.+)$/, (_, detail) => `Import failed: ${translateErrorDetail(detail)}`],
[/^Initialisierung fehlgeschlagen: (.+)$/, (_, detail) => `Initialization failed: ${translateErrorDetail(detail)}`],
[/^Export fehlgeschlagen: (.+)$/, 'Export failed: $1'],
[/^Import fehlgeschlagen: (.+)$/, 'Import failed: $1'],
[/^Initialisierung fehlgeschlagen: (.+)$/, 'Initialization failed: $1'],
[/^Quelldatei-Schutz konnte nicht vorbereitet werden: (.+)$/, 'Source file protection could not be prepared: $1'],
[/^Clouddrop: API-Antwort war kein JSON (.+)$/, 'Clouddrop: API response was not JSON $1'],
[/^Clouddrop: Datei nicht lesbar: (.+)$/, 'Clouddrop: File cannot be read: $1'],
@@ -798,7 +675,6 @@
[/^Update-Server Antwort war kein JSON (.+)$/, 'Update server response was not JSON $1'],
[/^(\d+) Links kopiert$/, '$1 links copied'],
[/^(\d+) Link kopiert$/, '$1 link copied'],
[/^(\d+) weitere Fehler$/, '$1 more errors'],
[/^Wirklich alle (\d+) Links aus diesem Panel entfernen\?$/, 'Remove all $1 links from this panel?'],
[/^Ein ausgewählter Eintrag wird aus diesem Panel entfernt\.$/, 'One selected entry will be removed from this panel.'],
[/^(\d+) ausgewählte Einträge werden aus diesem Panel entfernt\.$/, '$1 selected entries will be removed from this panel.'],
@@ -813,7 +689,6 @@
[/^Neustart in (\d+)s\.\.\.$/, 'Restart in $1s...'],
[/^(\d+) Job zum erneuten Upload zurückgesetzt$/, '$1 job reset for upload'],
[/^(\d+) Jobs zum erneuten Upload zurückgesetzt$/, '$1 jobs reset for upload'],
[/^(\d+) Uploads könnten bei den Hostern bereits abgeschlossen sein\. Erneute Uploads können Duplikate erzeugen\.$/, '$1 uploads may already be complete on their hosts. Uploading them again can create duplicates.'],
[/^(\d+) Verlaufseintrag wird dauerhaft entfernt\.$/, '$1 history entry will be permanently removed.'],
[/^(\d+) Verlaufseinträge werden dauerhaft entfernt\.$/, '$1 history entries will be permanently removed.'],
[/^Aktives Ziel: (.+)$/, 'Active destination: $1'],
@@ -872,27 +747,12 @@
[/^Update v(.+) verfügbar\. Klicken zum Installieren\.$/, 'Update v$1 available. Click to install.']
]
: [
[/^Login successful, upload form ready \(file field: (.+)\)$/, 'Login ok, Upload-Form bereit (Dateifeld: $1)'],
[/^Plain JSON backup is not valid JSON: (.+)$/, 'Klartext-Backup ist kein gültiges JSON: $1'],
[/^Export failed: (.+)$/, (_, detail) => `Export fehlgeschlagen: ${translateErrorDetail(detail)}`],
[/^Import failed: (.+)$/, (_, detail) => `Import fehlgeschlagen: ${translateErrorDetail(detail)}`],
[/^Initialization failed: (.+)$/, (_, detail) => `Initialisierung fehlgeschlagen: ${translateErrorDetail(detail)}`],
[/^Update v(.+) available$/, 'Update v$1 verfügbar'],
[/^1 interrupted upload can be resumed\.$/, '1 unterbrochener Upload kann fortgesetzt werden.'],
[/^(\d+) interrupted uploads can be resumed\.$/, '$1 unterbrochene Uploads können fortgesetzt werden.'],
[/^Restored queue starts in (.+) s \((.+) jobs\)\.$/, 'Wiederhergestellte Warteschlange startet in $1 s ($2 Jobs).'],
[/^(\d+) added$/, '$1 hinzugefügt'],
[/^(\d+) already in the batch$/, '$1 bereits im Batch'],
[/^(\d+) without a valid account$/, '$1 ohne gültigen Account'],
[/^(\d+) of (\d+) files will be added\. (\d+) excluded by the filename filter\.$/, '$1 von $2 Dateien werden hinzugefügt. $3 durch den Dateinamenfilter ausgeschlossen.'],
[/^Session report with 1 upload exported$/, 'Sitzungsbericht mit 1 Upload exportiert'],
[/^Session report with (\d+) uploads exported$/, 'Sitzungsbericht mit $1 Uploads exportiert'],
[/^Sleep in (\d+)s\.\.\.$/, 'Ruhezustand in $1s...'],
[/^Shut down in (\d+)s\.\.\.$/, 'Herunterfahren in $1s...'],
[/^Restart in (\d+)s\.\.\.$/, 'Neustart in $1s...'],
[/^(\d+) job reset for upload$/, '$1 Job zum erneuten Upload zurückgesetzt'],
[/^(\d+) jobs reset for upload$/, '$1 Jobs zum erneuten Upload zurückgesetzt'],
[/^(\d+) more errors$/, '$1 weitere Fehler'],
[/^(\d+) history entry will be permanently removed\.$/, '$1 Verlaufseintrag wird dauerhaft entfernt.'],
[/^(\d+) history entries will be permanently removed\.$/, '$1 Verlaufseinträge werden dauerhaft entfernt.'],
[/^Active on port (\d+) — 1 client connected$/, 'Aktiv auf Port $1 — 1 Client verbunden'],
+13 -132
View File
@@ -148,7 +148,7 @@
</div>
<div class="update-dialog-copy">
<h2 id="updateDialogTitle">Eine neue Version ist verfügbar</h2>
<p id="updateMessage" aria-live="polite"></p>
<p id="updateMessage"></p>
</div>
<div class="update-release-notes" id="updateReleaseNotes" hidden>
<div class="update-release-notes-title">Changelog</div>
@@ -209,10 +209,10 @@
<div class="view-sidebar-summary view-sidebar-summary-block hoster-summary" id="hosterSummary">Keine Upload-Ziele ausgewählt</div>
</div>
<div class="upload-telemetry" id="uploadTelemetry" aria-label="Upload-Statistik">
<div class="upload-telemetry-row"><span class="upload-telemetry-label">Verbleibend</span><strong class="upload-telemetry-value upload-rolling-value" id="uploadTelemetryRemaining" data-numeric-value="0" aria-label="0"><span>0</span></strong></div>
<div class="upload-telemetry-row"><span class="upload-telemetry-label">Gesamt</span><strong class="upload-telemetry-value upload-rolling-value" id="uploadTelemetryTotal" data-numeric-value="0" aria-label="0"><span>0</span></strong></div>
<div class="upload-telemetry-row"><span class="upload-telemetry-label">Läuft</span><strong class="upload-telemetry-value upload-rolling-value" id="uploadTelemetryRunning" data-numeric-value="0" aria-label="0"><span>0</span></strong></div>
<div class="upload-telemetry-row"><span class="upload-telemetry-label">Verbindungen</span><strong class="upload-telemetry-value upload-rolling-value" id="uploadTelemetryConnections" data-numeric-value="0" aria-label="0"><span>0</span></strong></div>
<div class="upload-telemetry-row"><span class="upload-telemetry-label">Verbleibend</span><strong class="upload-telemetry-value upload-rolling-value" id="uploadTelemetryRemaining" data-numeric-value="0" aria-label="0"><span>0</span></strong></div>
<div class="upload-telemetry-row"><span class="upload-telemetry-label">Läuft</span><strong class="upload-telemetry-value upload-rolling-value" id="uploadTelemetryRunning" data-numeric-value="0" aria-label="0"><span>0</span></strong></div>
<div class="upload-telemetry-row"><span class="upload-telemetry-label">Fertig</span><strong class="upload-telemetry-value upload-rolling-value" id="uploadTelemetryCompleted" data-numeric-value="0" aria-label="0"><span>0</span></strong></div>
<div class="upload-telemetry-row"><span class="upload-telemetry-label">Fehler</span><strong class="upload-telemetry-value upload-rolling-value" id="uploadTelemetryFailed" data-numeric-value="0" aria-label="0"><span>0</span></strong></div>
<div class="upload-telemetry-row"><span class="upload-telemetry-label">Geschwindigkeit</span><strong class="upload-telemetry-value" id="uploadTelemetrySpeed" aria-label="0 B/s">0 B/s</strong></div>
@@ -423,32 +423,6 @@
</div>
</div>
<div class="health-check-results account-health-results" id="healthCheckResults"></div>
<section class="hoster-health-overview" id="hosterHealthOverview" role="region" aria-labelledby="hosterHealthTitle" aria-describedby="hosterHealthHint">
<div class="hoster-health-heading">
<h3 id="hosterHealthTitle">Hoster-Gesundheit</h3>
<p id="hosterHealthHint">Lokale Werte aus höchstens 50 Batches. Der effektive historische Durchsatz kann Wartezeiten und Wiederholungen enthalten.</p>
</div>
<div class="hoster-health-scroll" tabindex="0">
<table>
<caption class="hoster-health-caption">Hoster-Gesundheit aus lokalem Verlauf und aktuellem Accountstatus</caption>
<thead>
<tr>
<th scope="col">Hoster</th>
<th scope="col">Stichprobe</th>
<th scope="col">Erfolg / Fehler / Übersprungen</th>
<th scope="col">Erfolgsrate</th>
<th scope="col" data-health-column="throughput">Effektiver historischer Durchsatz</th>
<th scope="col">Letzter Erfolg</th>
<th scope="col">Fehler (7 Tage)</th>
<th scope="col">Account-Probleme</th>
</tr>
</thead>
<tbody id="hosterHealthBody">
<tr><td colspan="8" data-hoster-health-empty>Wird geladen…</td></tr>
</tbody>
</table>
</div>
</section>
<div class="accounts-list" id="accountsList"></div>
<div class="accounts-list-footer" id="accountsListFooter" style="display:none">
<button class="btn btn-secondary" id="toggleAllAccountsBtn">Alle ausklappen</button>
@@ -456,8 +430,8 @@
</main>
</div>
<div class="modal-overlay" id="accountModal" style="display:none" aria-hidden="true">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="accountModalTitle" aria-describedby="accountModalSubtitle" tabindex="-1">
<div class="modal-overlay" id="accountModal" style="display:none">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="accountModalTitle" aria-describedby="accountModalSubtitle">
<div class="modal-header">
<div>
<h3 id="accountModalTitle">Account hinzufügen</h3>
@@ -484,8 +458,8 @@
</div>
</div>
<div class="modal-overlay" id="jobLogModal" style="display:none" aria-hidden="true">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="jobLogTitle" tabindex="-1" style="width:min(820px,96%);max-height:80vh;display:flex;flex-direction:column">
<div class="modal-overlay" id="jobLogModal" style="display:none">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="jobLogTitle" style="width:min(820px,96%);max-height:80vh;display:flex;flex-direction:column">
<div class="modal-header">
<div><h3 id="jobLogTitle">Upload-Log</h3></div>
<button class="icon-btn" id="closeJobLogBtn" aria-label="Schließen">&times;</button>
@@ -500,8 +474,8 @@
</div>
</div>
<div class="modal-overlay" id="deleteAccountModal" style="display:none" aria-hidden="true">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="deleteAccountTitle" aria-describedby="deleteAccountMessage" tabindex="-1" style="width:min(400px,100%)">
<div class="modal-overlay" id="deleteAccountModal" style="display:none">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="deleteAccountTitle" aria-describedby="deleteAccountMessage" style="width:min(400px,100%)">
<div class="modal-header">
<div><h3 id="deleteAccountTitle">Account löschen?</h3></div>
<button class="icon-btn" id="closeDeleteModalBtn" aria-label="Schließen">&times;</button>
@@ -517,7 +491,7 @@
</div>
<div class="modal-overlay app-alert-modal" id="appAlertModal" style="display:none" aria-hidden="true">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="appAlertTitle" aria-describedby="appAlertMessage" tabindex="-1" style="width:min(420px,100%)">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="appAlertTitle" aria-describedby="appAlertMessage" style="width:min(420px,100%)">
<div class="modal-header">
<div><h3 id="appAlertTitle">Hinweis</h3></div>
<button class="icon-btn" id="appAlertCloseBtn" aria-label="Schließen">&times;</button>
@@ -534,7 +508,7 @@
</div>
<div class="modal-overlay" id="historyClearModal" style="display:none" aria-hidden="true">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="historyClearModalTitle" aria-describedby="historyClearModalMessage" tabindex="-1" style="width:min(440px,100%)">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="historyClearModalTitle" aria-describedby="historyClearModalMessage" style="width:min(440px,100%)">
<div class="modal-header">
<div><h3 id="historyClearModalTitle">Verlauf löschen?</h3></div>
<button class="icon-btn" id="closeHistoryClearModalBtn" aria-label="Schließen">&times;</button>
@@ -667,104 +641,15 @@
<button class="btn btn-xs btn-secondary" id="cancelStartupResumeBtn">Abbrechen</button>
</div>
<div class="modal-overlay" id="batchCompletionModal" style="display:none" aria-hidden="true">
<div class="modal-card batch-completion-card" role="dialog" aria-modal="true" aria-labelledby="batchCompletionTitle" aria-describedby="batchCompletionSummary" tabindex="-1">
<div class="modal-header batch-completion-header">
<div class="batch-completion-heading">
<span class="batch-completion-outcome" id="batchCompletionOutcome">Erfolgreich</span>
<h3 id="batchCompletionTitle">Batch abgeschlossen</h3>
<p id="batchCompletionSummary">Der Upload-Batch wurde abgeschlossen.</p>
</div>
<button class="icon-btn" id="batchCompletionHeaderCloseBtn" aria-label="Schließen">&times;</button>
</div>
<div class="modal-body batch-completion-body">
<dl class="batch-completion-transfer" aria-label="Übertragung">
<div><dt>Dauer</dt><dd id="batchCompletionDuration">00:00:00</dd></div>
<div><dt>Erfolgreich übertragen</dt><dd id="batchCompletionSuccessfulBytes">0 B</dd></div>
<div><dt>Durchschnitt</dt><dd id="batchCompletionAverageSpeed">0 B/s</dd></div>
</dl>
<div class="batch-completion-metric-sections">
<section class="batch-completion-section" aria-labelledby="batchCompletionFilesTitle">
<h4 id="batchCompletionFilesTitle">Dateien</h4>
<dl class="batch-completion-metrics">
<div><dt>Gesamt</dt><dd id="batchCompletionFilesTotal">0</dd></div>
<div><dt>Vollständig erfolgreich</dt><dd id="batchCompletionFilesFullySucceeded">0</dd></div>
<div><dt>Teilweise erfolgreich</dt><dd id="batchCompletionFilesPartiallySucceeded">0</dd></div>
<div><dt>Fehlgeschlagen</dt><dd id="batchCompletionFilesFailed">0</dd></div>
</dl>
</section>
<section class="batch-completion-section" aria-labelledby="batchCompletionJobsTitle">
<h4 id="batchCompletionJobsTitle">Aufträge</h4>
<dl class="batch-completion-metrics batch-completion-job-metrics">
<div><dt>Gesamt</dt><dd id="batchCompletionJobsTotal">0</dd></div>
<div><dt>Erfolgreich</dt><dd id="batchCompletionJobsSucceeded">0</dd></div>
<div><dt>Fehlgeschlagen</dt><dd id="batchCompletionJobsFailed">0</dd></div>
<div><dt>Übersprungen</dt><dd id="batchCompletionJobsSkipped">0</dd></div>
<div><dt>Abgebrochen</dt><dd id="batchCompletionJobsAborted">0</dd></div>
</dl>
</section>
<section class="batch-completion-section" aria-labelledby="batchCompletionCleanupTitle">
<h4 id="batchCompletionCleanupTitle">Quelldateien</h4>
<dl class="batch-completion-metrics">
<div><dt>Angefordert</dt><dd id="batchCompletionCleanupRequested">0</dd></div>
<div><dt>Gelöscht</dt><dd id="batchCompletionCleanupDeleted">0</dd></div>
<div><dt>Blockiert</dt><dd id="batchCompletionCleanupBlocked">0</dd></div>
<div><dt>Fehlgeschlagen</dt><dd id="batchCompletionCleanupFailed">0</dd></div>
</dl>
</section>
</div>
<section class="batch-completion-section batch-completion-hosters" aria-labelledby="batchCompletionHostersTitle">
<h4 id="batchCompletionHostersTitle">Hosterübersicht</h4>
<div class="batch-completion-table-scroll" tabindex="0">
<table>
<thead>
<tr>
<th scope="col">Hoster</th>
<th scope="col">Aufträge</th>
<th scope="col">Erfolgreich</th>
<th scope="col">Fehlgeschlagen</th>
<th scope="col">Übersprungen</th>
<th scope="col">Abgebrochen</th>
<th scope="col">Übertragen</th>
</tr>
</thead>
<tbody id="batchCompletionHostersBody"></tbody>
</table>
</div>
</section>
<section class="batch-completion-section batch-completion-errors" id="batchCompletionErrorsSection" aria-labelledby="batchCompletionErrorsTitle" hidden>
<div class="batch-completion-section-heading">
<h4 id="batchCompletionErrorsTitle">Fehlerbeispiele</h4>
<span id="batchCompletionErrorsCount">0</span>
</div>
<ol id="batchCompletionErrorsList"></ol>
<p id="batchCompletionErrorsMore" hidden></p>
</section>
</div>
<div class="modal-footer batch-completion-footer">
<div class="batch-completion-export-actions">
<button class="btn btn-secondary" id="batchCompletionExportJsonBtn">JSON exportieren</button>
<button class="btn btn-secondary" id="batchCompletionExportCsvBtn">Fehler-CSV exportieren</button>
</div>
<button class="btn btn-primary" id="batchCompletionCloseBtn">Schließen</button>
</div>
</div>
</div>
<div class="shutdown-overlay" id="shutdownOverlay" style="display:none" aria-hidden="true">
<div class="shutdown-box" role="dialog" aria-modal="true" aria-labelledby="shutdownMessage" tabindex="-1">
<div class="shutdown-box" role="dialog" aria-modal="true" aria-labelledby="shutdownMessage">
<p id="shutdownMessage">System wird heruntergefahren in <span id="shutdownSeconds">60</span>s...</p>
<button class="btn btn-danger" id="cancelShutdownBtn">Abbrechen</button>
</div>
</div>
<div class="modal-overlay" id="hosterModal" style="display:none" aria-hidden="true">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="hosterModalTitle" aria-describedby="hosterModalDescription" tabindex="-1">
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="hosterModalTitle" aria-describedby="hosterModalDescription">
<div class="modal-header">
<div>
<h3 id="hosterModalTitle">Upload-Ziele auswählen</h3>
@@ -781,7 +666,6 @@
<dl class="import-plan-summary" id="importPlanSummary" aria-label="Import-Vorabprüfung">
<div><dt>Kandidaten</dt><dd id="importPlanCandidates">0</dd></div>
<div><dt>Bereits vorhanden / dupliziert</dt><dd id="importPlanDuplicates">0</dd></div>
<div><dt>Durch Dateinamenfilter ausgeschlossen</dt><dd id="importPlanFiltered">0</dd></div>
<div><dt>Fehlend / unlesbar / leer</dt><dd id="importPlanUnavailable">0</dd></div>
<div><dt>Akzeptierte Dateien</dt><dd id="importPlanAccepted">0</dd></div>
<div><dt>Ausgewählte Ziele</dt><dd id="importPlanTargets">0</dd></div>
@@ -807,10 +691,7 @@
<script src="../lib/throttle-timer.js"></script>
<script src="../lib/serialized-runner.js"></script>
<script src="../lib/speed-history.js"></script>
<script src="../lib/upload-recovery.js"></script>
<script src="../lib/filename-filter.js"></script>
<script src="../lib/import-preflight.js"></script>
<script src="../lib/upload-schedule.js"></script>
<script src="account-submit.js"></script>
<script src="account-status.js"></script>
<script src="history-status.js"></script>
+50 -575
View File
@@ -29,7 +29,6 @@
body {
font-family: 'Aptos', 'Segoe UI Variable Text', 'Bahnschrift', sans-serif;
background: var(--bg-primary);
cursor: default;
height: 100vh;
overflow: hidden;
color: var(--text);
@@ -499,7 +498,7 @@ body.col-resizing, body.col-resizing * { cursor: col-resize !important; user-sel
font-size: 10px;
font-weight: 600;
}
.status-badge.status-preview { color: var(--success); background: rgba(0, 184, 148, 0.15); }
.status-badge.status-preview { color: var(--text-muted); }
.status-badge.status-queued { color: var(--text-muted); background: rgba(255,255,255,0.05); }
.status-badge.status-getting-server { color: var(--accent); }
.status-badge.status-uploading { color: #5dabf7; background: rgba(93, 171, 247, 0.15); }
@@ -735,239 +734,6 @@ body.col-resizing, body.col-resizing * { cursor: col-resize !important; user-sel
gap: 8px;
margin-bottom: 12px;
}
.batch-completion-card {
width: min(920px, 100%);
max-height: min(calc(100vh - 48px), 820px);
}
.batch-completion-header {
align-items: flex-start;
}
.batch-completion-heading {
min-width: 0;
}
.batch-completion-outcome {
display: inline-flex;
align-items: center;
min-height: 22px;
margin-bottom: 7px;
padding: 3px 8px;
border: 1px solid rgba(52, 211, 153, .38);
border-radius: 999px;
background: rgba(52, 211, 153, .12);
color: var(--success);
font-size: 11px;
font-weight: 700;
letter-spacing: .04em;
text-transform: uppercase;
}
#batchCompletionModal[data-outcome="mixed"] .batch-completion-outcome {
border-color: rgba(245, 158, 11, .42);
background: rgba(245, 158, 11, .12);
color: var(--warning);
}
.batch-completion-body {
display: grid;
gap: 12px;
min-width: 0;
overflow-x: hidden;
}
.batch-completion-transfer,
.batch-completion-metrics {
margin: 0;
}
.batch-completion-transfer {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
}
.batch-completion-transfer > div,
.batch-completion-metrics > div {
min-width: 0;
border: 1px solid var(--border);
border-radius: 8px;
background: rgba(255, 255, 255, .025);
}
.batch-completion-transfer > div {
padding: 10px 12px;
}
.batch-completion-transfer dt,
.batch-completion-metrics dt {
color: var(--text-muted);
font-size: 11px;
}
.batch-completion-transfer dd,
.batch-completion-metrics dd {
margin: 4px 0 0;
color: var(--text-primary);
font-size: 16px;
font-variant-numeric: tabular-nums;
font-weight: 700;
}
.batch-completion-metric-sections {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 10px;
}
.batch-completion-section {
min-width: 0;
padding: 12px;
border: 1px solid var(--border);
border-radius: 10px;
background: rgba(255, 255, 255, .018);
}
.batch-completion-section h4 {
margin: 0 0 9px;
font-size: 13px;
}
.batch-completion-metrics {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 7px;
}
.batch-completion-metrics > div {
padding: 8px 9px;
}
.batch-completion-job-metrics > div:first-child {
grid-column: 1 / -1;
}
.batch-completion-table-scroll {
max-width: 100%;
overflow: auto;
border: 1px solid var(--border);
border-radius: 8px;
}
.batch-completion-table-scroll:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.batch-completion-table-scroll table {
width: 100%;
min-width: 650px;
border-collapse: collapse;
}
.batch-completion-table-scroll th,
.batch-completion-table-scroll td {
padding: 7px 9px;
border-bottom: 1px solid var(--border);
text-align: right;
white-space: nowrap;
}
.batch-completion-table-scroll th {
color: var(--text-muted);
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
}
.batch-completion-table-scroll th:first-child,
.batch-completion-table-scroll td:first-child {
text-align: left;
}
.batch-completion-table-scroll tbody tr:last-child td {
border-bottom: 0;
}
.batch-completion-section-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.batch-completion-section-heading h4 {
margin-bottom: 0;
}
.batch-completion-section-heading span {
min-width: 24px;
padding: 2px 7px;
border-radius: 999px;
background: rgba(239, 68, 68, .14);
color: var(--danger);
text-align: center;
font-size: 11px;
font-weight: 700;
}
.batch-completion-errors ol {
display: grid;
gap: 8px;
margin: 10px 0 0;
padding: 0;
list-style: none;
}
.batch-completion-errors li {
min-width: 0;
padding: 9px 10px;
border: 1px solid rgba(239, 68, 68, .22);
border-radius: 8px;
background: rgba(239, 68, 68, .055);
}
.batch-completion-error-head,
.batch-completion-error-meta {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px 10px;
}
.batch-completion-error-file {
overflow-wrap: anywhere;
}
.batch-completion-error-meta {
margin-top: 4px;
color: var(--text-muted);
font-size: 11px;
}
.batch-completion-error-message {
margin: 7px 0 0;
color: var(--text-secondary);
overflow-wrap: anywhere;
}
.batch-completion-error-uncertain {
color: var(--warning);
font-weight: 700;
}
#batchCompletionErrorsMore {
margin: 9px 0 0;
color: var(--text-muted);
font-size: 12px;
}
.batch-completion-footer,
.batch-completion-export-actions {
display: flex;
align-items: center;
gap: 8px;
}
.batch-completion-footer {
flex-wrap: wrap;
}
.batch-completion-export-actions {
flex-wrap: wrap;
}
@media (max-width: 820px) {
.batch-completion-card {
max-height: calc(100vh - 24px);
}
.batch-completion-metric-sections {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.batch-completion-metric-sections > :last-child {
grid-column: 1 / -1;
}
}
@media (max-width: 620px) {
.batch-completion-transfer,
.batch-completion-metric-sections {
grid-template-columns: 1fr;
}
.batch-completion-metric-sections > :last-child {
grid-column: auto;
}
.batch-completion-footer,
.batch-completion-export-actions {
align-items: stretch;
width: 100%;
}
.batch-completion-footer > .btn,
.batch-completion-export-actions .btn {
flex: 1 1 auto;
}
}
.hoster-modal-list {
display: grid;
gap: 10px;
@@ -981,13 +747,13 @@ body.col-resizing, body.col-resizing * { cursor: col-resize !important; user-sel
.import-plan-summary > div {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
align-items: start;
gap: 8px;
min-height: 38px;
padding: 8px 10px;
border: 1px solid var(--border);
border-radius: 8px;
background: rgba(255, 255, 255, 0.025);
background: rgba(255, 255, 255, 0.02);
}
.import-plan-summary dt {
min-width: 0;
@@ -1002,6 +768,9 @@ body.col-resizing, body.col-resizing * { cursor: col-resize !important; user-sel
color: var(--text);
font-variant-numeric: tabular-nums;
}
.hoster-modal-list + .import-plan-summary + .modal-hint {
margin: 10px 0 2px;
}
.hoster-option {
display: flex;
align-items: center;
@@ -1266,7 +1035,6 @@ body.col-resizing, body.col-resizing * { cursor: col-resize !important; user-sel
.panel-status { font-size: 10px; padding: 2px 8px; border-radius: 3px; }
.panel-status.active { background: rgba(0, 184, 148, 0.2); color: var(--success); }
.panel-status.inactive { background: rgba(255, 255, 255, 0.05); color: var(--text-dim); }
.panel-status.warning { background: rgba(245, 166, 35, 0.16); color: var(--warning); }
.hoster-panel-body { padding: 0 14px 14px; }
.settings-divider { height: 1px; background: var(--border); margin: 12px 0; }
@@ -1426,61 +1194,6 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
}
.settings-subpage .settings-grid-mini .checkbox-row label { min-width: 0; cursor: pointer; }
.settings-subpage .settings-grid-mini .checkbox-row input[type="checkbox"] { order: 2; width: 18px; height: 18px; accent-color: var(--accent); cursor: pointer; }
.upload-schedule-panel {
display: grid;
gap: 10px;
padding: 10px;
border: 1px solid color-mix(in srgb, var(--border) 90%, transparent);
border-radius: 9px;
background: color-mix(in srgb, var(--bg-input) 62%, transparent);
}
.upload-schedule-panel .settings-option { margin: 0; }
.upload-schedule-days {
display: grid;
grid-template-columns: repeat(7, minmax(48px, 1fr));
gap: 6px;
}
.upload-schedule-days label { min-width: 0; cursor: pointer; }
.upload-schedule-days input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.upload-schedule-days span {
display: grid;
place-items: center;
min-height: 34px;
border: 1px solid var(--border);
border-radius: 7px;
background: var(--bg-input);
color: var(--text-dim);
font-size: 12px;
font-weight: 700;
transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease, opacity 160ms ease;
}
.upload-schedule-days input:checked + span {
border-color: var(--accent);
background: color-mix(in srgb, var(--accent) 24%, var(--bg-input));
color: var(--text);
}
.upload-schedule-days input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
.upload-schedule-days input:disabled + span { opacity: 0.42; cursor: default; }
.upload-schedule-times {
display: grid;
grid-template-columns: minmax(150px, 1fr) minmax(150px, 1fr) minmax(220px, 1.5fr);
gap: 10px;
align-items: end;
}
.upload-schedule-times label { display: grid; gap: 6px; color: var(--text); font-size: 12px; font-weight: 600; }
.upload-schedule-times .hs-input { width: 100%; max-width: none; }
.upload-schedule-times .hint { align-self: center; line-height: 1.45; }
.upload-schedule-status {
min-height: 18px;
margin: 0;
color: var(--text-dim);
font-size: 11px;
line-height: 1.45;
}
.settings-hoster-pointer {
margin-top: 16px;
padding: 11px 13px;
@@ -1607,29 +1320,6 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
.account-hoster-settings-header .panel-arrow { font-size: 10px; color: var(--text-dim); }
.account-hoster-settings-body-inner { padding: 4px 12px 10px; }
.account-hoster-settings-body-inner .account-hoster-option-row {
grid-column: 1 / -1;
display: grid;
grid-template-columns: 150px minmax(0, 1fr) auto;
align-items: center;
column-gap: 12px;
}
.account-hoster-settings-body-inner .account-hoster-option-row label {
min-width: 0;
}
.account-hoster-settings-body-inner .account-hoster-option-row .hint {
grid-column: 2;
line-height: 1.45;
}
.account-hoster-settings-body-inner .account-hoster-option-row input[type="checkbox"] {
grid-column: 3;
grid-row: 1;
justify-self: end;
}
.settings-hoster-pointer {
margin-top: 12px;
padding: 10px 14px;
@@ -2079,6 +1769,40 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
.tab-indicator {
transition-duration: 280ms !important;
}
.view-sidebar-indicator,
.settings-nav-indicator {
transition-duration: 220ms !important;
}
.language-picker-indicator {
transition-duration: 240ms !important;
}
.progress-bar-fill {
transition-duration: 360ms !important;
}
.menu-opening {
animation-duration: 180ms !important;
}
.menu-closing {
animation-duration: 160ms !important;
}
.account-collapse {
transition-duration: 220ms, 160ms, 0s !important;
}
.account-hoster-group-header .panel-arrow,
.account-hoster-settings-header .panel-arrow {
transition-duration: 180ms !important;
}
}
.online-backup-panel {
@@ -2162,9 +1886,6 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
.settings-navigation { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.settings-content { overflow: visible; }
.settings-grid-mini { grid-template-columns: 1fr; }
.upload-schedule-days { grid-template-columns: repeat(4, minmax(48px, 1fr)); }
.upload-schedule-times { grid-template-columns: 1fr 1fr; }
.upload-schedule-times .hint { grid-column: 1 / -1; }
.online-backup-key-row {
grid-template-columns: 1fr;
}
@@ -2683,10 +2404,6 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
display: grid;
}
.update-progress[hidden] {
display: none;
}
.update-release-notes {
grid-column: 1 / -1;
max-height: 150px;
@@ -3405,7 +3122,8 @@ input[type="checkbox"] {
.settings-nav-button {
min-height: 36px;
padding: 7px 10px;
border: 1px solid var(--border);
border: 1px solid transparent;
border-left: 1px solid transparent;
border-radius: 6px;
color: var(--text-muted);
font-size: 12px;
@@ -3649,103 +3367,6 @@ input[type="checkbox"] {
border-color: var(--danger);
}
.filename-filter-panel {
display: grid;
gap: 10px;
}
.filename-filter-builder {
display: grid;
gap: 12px;
padding: 14px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--bg-card);
transition: opacity 160ms ease, border-color 160ms ease;
}
.filename-filter-builder.disabled {
opacity: 0.55;
}
.filename-filter-policy {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.filename-filter-field {
display: grid;
gap: 6px;
}
.filename-filter-field label {
color: var(--text-muted);
font-size: 13px;
font-weight: 600;
}
.filename-filter-policy .hs-input,
.filename-filter-condition .hs-input,
.filename-filter-condition .key-input {
min-width: 0;
max-width: none;
width: 100%;
}
.filename-filter-conditions {
display: grid;
gap: 8px;
}
.filename-filter-condition {
display: grid;
grid-template-columns: minmax(240px, 1.35fr) minmax(150px, 0.65fr) auto;
gap: 8px;
align-items: end;
}
.filename-filter-rule-field {
display: grid;
gap: 6px;
min-width: 0;
}
.filename-filter-rule-label {
color: var(--text-muted);
font-size: 13px;
font-weight: 600;
}
.filename-filter-footer {
display: flex;
gap: 10px;
align-items: center;
justify-content: space-between;
}
.filename-filter-footer .hint {
margin-left: auto;
text-align: right;
}
@media (max-width: 820px) {
.filename-filter-policy,
.filename-filter-condition {
grid-template-columns: 1fr;
}
.filename-filter-footer {
align-items: stretch;
flex-direction: column;
}
.filename-filter-footer .hint {
margin-left: 0;
text-align: left;
}
}
.settings-option-description,
.hint {
color: var(--text-dim);
@@ -3802,7 +3423,7 @@ input[type="checkbox"] {
}
#settingsSearchInput {
font-size: 11px;
font-size: 13px;
}
.settings-nav-button {
@@ -3973,115 +3594,6 @@ input[type="checkbox"] {
padding: 10px 16px 0;
}
.hoster-health-overview {
flex: 0 0 auto;
min-width: 0;
max-width: 100%;
padding: 10px 16px 0;
overflow: hidden;
}
.hoster-health-heading {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
margin-bottom: 7px;
}
.hoster-health-heading h3 {
flex: 0 0 auto;
margin: 0;
color: var(--text);
font-size: 13px;
line-height: 1.3;
}
.hoster-health-heading p {
min-width: 0;
margin: 0;
color: var(--text-dim);
font-size: 10px;
line-height: 1.35;
text-align: right;
}
.hoster-health-scroll {
max-width: 100%;
max-height: 190px;
overflow: auto;
overscroll-behavior: contain;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg-secondary);
}
.hoster-health-scroll:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.hoster-health-scroll table {
width: 100%;
min-width: 880px;
border-collapse: collapse;
table-layout: fixed;
font-size: 10px;
font-variant-numeric: tabular-nums;
}
.hoster-health-caption {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.hoster-health-scroll th,
.hoster-health-scroll td {
padding: 6px 8px;
border-bottom: 1px solid var(--border);
color: var(--text-muted);
text-align: left;
vertical-align: middle;
}
.hoster-health-scroll thead th {
position: sticky;
top: 0;
z-index: 1;
background: var(--bg-raised);
color: var(--text-dim);
font-size: 9px;
font-weight: 600;
line-height: 1.25;
text-transform: uppercase;
}
.hoster-health-scroll tbody th {
color: var(--text);
font-weight: 600;
}
.hoster-health-scroll tbody tr:last-child > * {
border-bottom: 0;
}
.hoster-health-scroll tbody tr:hover > * {
background: var(--bg-raised);
}
.hoster-health-scroll [data-hoster-health-empty] {
height: 38px;
color: var(--text-dim);
text-align: center;
}
.accounts-list {
flex: 1 1 auto;
min-height: 0;
@@ -4208,47 +3720,6 @@ input[type="checkbox"] {
box-shadow: 0 24px 64px rgba(0, 0, 0, .48);
}
#accountModal.account-modal-opening {
animation: account-modal-dim-in 360ms cubic-bezier(.16, 1, .3, 1) both;
}
#accountModal.account-modal-opening > .modal-card {
transform-origin: top center;
animation: account-modal-unfold 360ms cubic-bezier(.16, 1, .3, 1) both;
}
#accountModal.account-modal-closing {
pointer-events: none;
animation: account-modal-dim-out 300ms cubic-bezier(.4, 0, .2, 1) both;
}
#accountModal.account-modal-closing > .modal-card {
transform-origin: top center;
animation: account-modal-fold 300ms cubic-bezier(.4, 0, .2, 1) both;
}
@keyframes account-modal-dim-in {
from { background: rgba(0, 0, 0, 0); }
to { background: rgba(0, 0, 0, .6); }
}
@keyframes account-modal-dim-out {
from { background: rgba(0, 0, 0, .6); }
to { background: rgba(0, 0, 0, 0); }
}
@keyframes account-modal-unfold {
0% { opacity: 0; clip-path: inset(0 0 18% 0 round 12px); transform: translateY(-16px) scale(.985); }
55% { opacity: .96; }
100% { opacity: 1; clip-path: inset(0 round 12px); transform: translateY(0) scale(1); }
}
@keyframes account-modal-fold {
0% { opacity: 1; clip-path: inset(0 round 12px); transform: translateY(0) scale(1); }
45% { opacity: .9; }
100% { opacity: 0; clip-path: inset(0 0 18% 0 round 12px); transform: translateY(-16px) scale(.985); }
}
.modal-header,
.modal-footer {
padding: 16px 18px;
@@ -4566,9 +4037,13 @@ input[type="checkbox"] {
}
.upload-speed-sparkline {
width: 118px;
display: grid;
grid-template-columns: minmax(0, 1fr) max-content;
width: 64px;
display: flex;
justify-content: flex-end;
}
.upload-speed-sparkline canvas {
display: none;
}
.settings-layout {