fix: make clipboard collector navigation opt-in

This commit is contained in:
Sucukdeluxe
2026-09-06 12:24:20 +02:00
parent bd11b57fb8
commit 8957aa3c23
17 changed files with 174 additions and 14 deletions
+2 -1
View File
@@ -114,7 +114,8 @@ export function defaultSettings(): AppSettings {
proxyConnectionsPerDownload: 32,
updateRepo: DEFAULT_UPDATE_REPO,
autoUpdateCheck: true,
clipboardWatch: false,
clipboardWatch: false,
switchToCollectorOnClipboard: false,
minimizeToTray: false,
theme: "dark" as const,
themePreference: "dark" as const,
+1
View File
@@ -213,6 +213,7 @@ export function createRendererSettings(settings: AppSettings): RendererSettings
updateRepo: settings.updateRepo,
autoUpdateCheck: settings.autoUpdateCheck,
clipboardWatch: settings.clipboardWatch,
switchToCollectorOnClipboard: settings.switchToCollectorOnClipboard,
minimizeToTray: settings.minimizeToTray,
theme: settings.theme,
themePreference: settings.themePreference,
+2 -1
View File
@@ -622,7 +622,8 @@ export function normalizeSettings(settings: AppSettings): AppSettings {
proxyConnectionsPerDownload: clampNumber(settings.proxyConnectionsPerDownload, defaults.proxyConnectionsPerDownload, 2, 80),
autoUpdateCheck: Boolean(settings.autoUpdateCheck),
updateRepo: migrateUpdateRepo(asText(settings.updateRepo), defaults.updateRepo),
clipboardWatch: Boolean(settings.clipboardWatch),
clipboardWatch: Boolean(settings.clipboardWatch),
switchToCollectorOnClipboard: settings.switchToCollectorOnClipboard === true,
minimizeToTray: Boolean(settings.minimizeToTray),
logStorageLocation: VALID_LOG_STORAGE_LOCATIONS.has(settings.logStorageLocation)
? settings.logStorageLocation
+9 -6
View File
@@ -41,6 +41,7 @@ import {
} from "../shared/provider-daily-limits";
import { createAvailabilitySortCycle, preservePackageOrderForDisplay, sortPackageOrderByAvailability, sortPackageOrderByName } from "./package-order";
import { createPackageOrderState } from "./package-order-state";
import { shouldFocusCollectorImport, type CollectorImportSource } from "./collector-navigation";
import { getPackagesWithOfflineLinks } from "../shared/offline-packages";
import type { OfflineSkipScope } from "../shared/types";
import { OfflineRemovalScopeChoice } from "./views/downloads/OfflineRemovalScopeChoice";
@@ -959,7 +960,7 @@ const emptySnapshot = (): UiSnapshot => ({
autoReconnect: false, reconnectWaitSeconds: 45, completedCleanupPolicy: "never",
maxParallel: 4, maxParallelExtract: 2, extractCpuPriority: "high", retryLimit: 0, offlineSkipScope: "archive", speedLimitEnabled: false, speedLimitKbps: 0, speedLimitMode: "global",
proxyDownloadEnabled: false, proxyListPath: "", proxyApiProxyIndex: 1, proxyConnectionsPerDownload: 32,
updateRepo: "", autoUpdateCheck: true, clipboardWatch: false, minimizeToTray: false,
updateRepo: "", autoUpdateCheck: true, clipboardWatch: false, switchToCollectorOnClipboard: false, minimizeToTray: false,
theme: "dark", themePreference: "dark", logStorageLocation: "appdata", collapseNewPackages: true, animatePackageDisclosure: true, historyRetentionMode: "permanent", historyMaxEntries: 500, historyMaxAgeDays: 0, autoSortPackagesByProgress: false, autoSkipExtracted: false, hideExtractedItems: true, confirmDeleteSelection: true, backupIncludeDownloads: false, backupIncludeRemoteDiagnostics: false,
notifyMention: "", notifyOnPackageCompleted: false, notifyOnPackageFailed: false, notifyOnRunFinished: false,
notifyPackageSuccessMode: "digest", notifyOnRemainingBelow: false, notifyRemainingThresholdGb: 50,
@@ -1884,7 +1885,7 @@ export function App(): ReactElement {
const collectorPersistenceBudgetRef = useRef<CollectorPersistenceBudget | null>(null);
const collectorEnrichmentGenerationsRef = useRef(new Map<string, number>());
const collectorEnrichmentRequestsRef = useRef(new Map<string, ReturnType<typeof beginCollectorEnrichment>>());
const importCollectorTextRef = useRef<(rawText: string) => Promise<void>>(() => Promise.resolve());
const importCollectorTextRef = useRef<(rawText: string, source?: CollectorImportSource) => Promise<void>>(() => Promise.resolve());
const activeTabRef = useRef<Tab>(tab);
const packageOrderRef = useRef<string[]>([]);
const packageOrderStateRef = useRef(createPackageOrderState());
@@ -2401,7 +2402,7 @@ export function App(): ReactElement {
loadInitialSnapshot();
unsubClipboard = window.rd.onClipboardDetected((links) => {
showToast(`Zwischenablage: ${links.length} Link(s) erkannt`, 3000);
void importCollectorTextRef.current(links.join("\n"));
void importCollectorTextRef.current(links.join("\n"), "clipboard");
});
unsubUpdateInstallProgress = window.rd.onUpdateInstallProgress((progress) => {
if (!mountedRef.current) {
@@ -4033,7 +4034,7 @@ export function App(): ReactElement {
});
}, [collapsedCollectorPackageIds, collectorPackages]);
const importCollectorText = async (rawText: string): Promise<void> => {
const importCollectorText = async (rawText: string, source: CollectorImportSource = "manual"): Promise<void> => {
if (!rawText.trim()) {
showToast("Keine Links eingegeben", 2200);
return;
@@ -4049,8 +4050,10 @@ export function App(): ReactElement {
return;
}
if (!mergeCollectorResult(prepared)) return;
setCollectorFilter("all");
setTab("collector");
if (shouldFocusCollectorImport(source, snapshotRef.current.settings.switchToCollectorOnClipboard)) {
setCollectorFilter("all");
setTab("collector");
}
const linkCount = prepared.packages.reduce((sum, pkg) => sum + pkg.links.length, 0);
showToast(`${prepared.packages.length} Paket(e), ${linkCount} Link(s) gesammelt`);
enrichCollectorResult(prepared.packages);
+5
View File
@@ -0,0 +1,5 @@
export type CollectorImportSource = "manual" | "clipboard";
export function shouldFocusCollectorImport(source: CollectorImportSource, switchToCollectorOnClipboard: boolean | undefined): boolean {
return source === "manual" || switchToCollectorOnClipboard === true;
}
+2
View File
@@ -13,6 +13,8 @@ const pairs = [
["Zielordner für heruntergeladene Dateien.", "Destination folder for downloaded files."], ["Fehlende Arbeitsordner beim Start anlegen", "Create missing work folders on startup"], ["Erstellt fehlende Download-, Entpack- und Videosammelordner sofort beim Speichern sowie bei jedem Programmstart. Optionale Ordner werden nur bei aktiver Funktion angelegt; vorhandene Inhalte bleiben unverändert.", "Creates missing download, extraction, and video library folders immediately when settings are saved and on every application start. Optional folders are created only when their feature is enabled; existing contents remain unchanged."],
["Beim Start automatisch fortsetzen", "Resume automatically on startup"], ["Zwischenablage überwachen", "Monitor clipboard"], ["Verlauf speichern", "Save history"], ["Nur aktuelle Session", "Current session only"], ["Nur letzte 100 Einträge", "Last 100 entries only"], ["Nur letzte 250 Einträge", "Last 250 entries only"], ["Dauerhaft", "Permanent"],
["Maximale Verlauf-Einträge", "Maximum history entries"], ["Einträge löschen älter als (Tage)", "Delete entries older than (days)"], ["Neue Pakete eingeklappt zeigen", "Show new packages collapsed"], ["Animationen", "Animations"],
["Bei erkannten Zwischenablage-Links zum Linksammler wechseln", "Switch to Link Collector when clipboard links are detected"],
["Standardmäßig aus: Links werden im Hintergrund gesammelt, ohne den aktuellen Tab zu wechseln. Gilt nur bei aktivierter Zwischenablage-Überwachung.", "Off by default: links are collected in the background without switching tabs. Applies only when clipboard monitoring is enabled."],
["In den Infobereich minimieren", "Minimize to tray"], ["Vor dem Löschen nachfragen", "Confirm before deleting"], ["Download-Liste mitsichern", "Include download list in backup"],
["Ferndiagnose-Einstellungen mitsichern", "Include remote diagnostics settings in backup"], ["Webhook-Adresse", "Webhook address"], ["Discord-Erwähnung (optional)", "Discord mention (optional)"],
["Melden, wenn ein Paket fertig ist", "Notify when a package completes"], ["Melden, wenn ein Paket fehlschlägt", "Notify when a package fails"], ["Melden, wenn alles fertig ist", "Notify when everything completes"],
@@ -623,7 +623,12 @@ export function buildSettingsFormViewModel({
help: "Beim Archivsatz werden alle noch offenen Teile desselben Mehrteil-Archivs übersprungen. Bereits abgeschlossene Downloads bleiben erhalten."
},
{ id: "autoResumeOnStart", kind: "switch", label: "Beim Start automatisch fortsetzen", value: settings.autoResumeOnStart },
{ id: "clipboardWatch", kind: "switch", label: "Zwischenablage überwachen", value: settings.clipboardWatch }
{ id: "clipboardWatch", kind: "switch", label: "Zwischenablage überwachen", value: settings.clipboardWatch },
{
id: "switchToCollectorOnClipboard", kind: "switch", label: "Bei erkannten Zwischenablage-Links zum Linksammler wechseln",
value: settings.switchToCollectorOnClipboard,
help: "Standardmäßig aus: Links werden im Hintergrund gesammelt, ohne den aktuellen Tab zu wechseln. Gilt nur bei aktivierter Zwischenablage-Überwachung."
}
]
},
{
+7 -5
View File
@@ -211,11 +211,12 @@ export interface AppSettings extends DailyStartSettings, ProxyDownloadSettings {
offlineSkipScope: OfflineSkipScope;
speedLimitEnabled: boolean;
speedLimitKbps: number;
speedLimitMode: SpeedMode;
updateRepo: string;
autoUpdateCheck: boolean;
clipboardWatch: boolean;
minimizeToTray: boolean;
speedLimitMode: SpeedMode;
updateRepo: string;
autoUpdateCheck: boolean;
clipboardWatch: boolean;
switchToCollectorOnClipboard: boolean;
minimizeToTray: boolean;
theme: AppTheme;
themePreference: ThemePreference;
logStorageLocation: LogStorageLocation;
@@ -352,6 +353,7 @@ export interface RendererSettings extends DailyStartSettings, ProxyDownloadSetti
updateRepo: string;
autoUpdateCheck: boolean;
clipboardWatch: boolean;
switchToCollectorOnClipboard: boolean;
minimizeToTray: boolean;
theme: AppTheme;
themePreference: ThemePreference;