diff --git a/frontend/src/lib/components/ResourceList.svelte b/frontend/src/lib/components/ResourceList.svelte
index e7a003e0..3914150c 100644
--- a/frontend/src/lib/components/ResourceList.svelte
+++ b/frontend/src/lib/components/ResourceList.svelte
@@ -92,6 +92,8 @@
import DisplayModeControls from '$lib/components/DisplayModeControls.svelte';
import UserVignette from '$lib/components/UserVignette.svelte';
import VirtualList from '$lib/components/VirtualList.svelte';
+ import { goto } from '$app/navigation';
+ import { resolve } from '$app/paths';
import { t } from '$lib/i18n/index.svelte';
import { ui } from '$lib/stores/ui.svelte';
import { files as filesStore } from '$lib/stores/files.svelte';
@@ -927,7 +929,13 @@
// element accepts drops — without it, `drop` never fires and
// the pointer shows the OS "no-drop" cursor.
e.preventDefault();
- if (e.dataTransfer) e.dataTransfer.dropEffect = enableSystemDrop ? 'copy' : 'none';
+ // `dropEffect = 'none'` would tell the browser to REJECT the
+ // drop before `drop` fires — the toast/notification path in
+ // `onSystemDrop` would never run for wrong-zone drops. Always
+ // accept at the pointer level; the drop handler decides
+ // whether to upload (`enableSystemDrop`) or fire the
+ // "go to Files" toast.
+ if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
}
function onSystemDragLeave(e: DragEvent) {
if (!isSystemDrag(e)) return;
@@ -949,7 +957,19 @@
'Uploads only work in Files — open the Files section and drop there.'
),
'warning',
- 6000
+ 6000,
+ true,
+ {
+ action: {
+ label: t('resource_list.wrong_drop_zone_action', 'Go to Files'),
+ // One-click recovery from a mis-drop: land the user in
+ // /files so they can re-drag from the OS. We don't
+ // re-attach the dropped files (browsers throw away
+ // DataTransfer once the drop event returns), so this
+ // is the best we can offer without a second drag.
+ onClick: () => goto(resolve('/files'))
+ }
+ }
);
}
}
diff --git a/frontend/src/lib/components/Toaster.svelte b/frontend/src/lib/components/Toaster.svelte
index ed13e130..72224f33 100644
--- a/frontend/src/lib/components/Toaster.svelte
+++ b/frontend/src/lib/components/Toaster.svelte
@@ -12,6 +12,18 @@
{#each ui.toasts as toast (toast.id)}
{toast.message}
+ {#if toast.action}
+ {
+ toast.action?.onClick();
+ ui.dismiss(toast.id);
+ }}
+ >
+ {toast.action.label}
+
+ {/if}
void;
+}
+
export interface Toast {
id: number;
message: string;
kind: ToastKind;
+ /** Optional inline action (e.g. "Go to Files" on a wrong-drop-zone toast). */
+ action?: ToastAction;
}
export interface Notification {
@@ -74,10 +83,21 @@ class UiStore {
/**
* Raise a toast and record a notification. `at` is stamped from the clock at
* call time; pass `record: false` for purely transient messages.
+ *
+ * The optional `opts.action` renders an inline button in the toast (e.g.
+ * "Go to Files" on a wrong-drop-zone warning); the callback fires on
+ * click and the toast auto-dismisses right after so a caller doesn't have
+ * to manage the id.
*/
- notify(message: string, kind: ToastKind = 'info', timeoutMs = 4000, record = true): number {
+ notify(
+ message: string,
+ kind: ToastKind = 'info',
+ timeoutMs = 4000,
+ record = true,
+ opts: { action?: ToastAction } = {}
+ ): number {
const id = ++this.#seq;
- this.toasts = [...this.toasts, { id, message, kind }];
+ this.toasts = [...this.toasts, { id, message, kind, action: opts.action }];
if (record) {
this.notifications = [
{ id, message, kind, at: Date.now(), read: false },
diff --git a/frontend/static/locales/ar.json b/frontend/static/locales/ar.json
index 5aec9991..659741cf 100644
--- a/frontend/static/locales/ar.json
+++ b/frontend/static/locales/ar.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "الموقع",
- "wrong_drop_zone_msg": "الرفع يعمل فقط في قسم الملفات — افتح قسم الملفات وأفلت العناصر هناك."
+ "wrong_drop_zone_msg": "الرفع يعمل فقط في قسم الملفات — افتح قسم الملفات وأفلت العناصر هناك.",
+ "wrong_drop_zone_action": "انتقل إلى الملفات"
}
}
diff --git a/frontend/static/locales/de.json b/frontend/static/locales/de.json
index a0ec447d..d77c7dd1 100644
--- a/frontend/static/locales/de.json
+++ b/frontend/static/locales/de.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "Speicherort",
- "wrong_drop_zone_msg": "Uploads funktionieren nur in Dateien — öffne den Bereich Dateien und lege die Elemente dort ab."
+ "wrong_drop_zone_msg": "Uploads funktionieren nur in Dateien — öffne den Bereich Dateien und lege die Elemente dort ab.",
+ "wrong_drop_zone_action": "Zu Dateien wechseln"
}
}
diff --git a/frontend/static/locales/en.json b/frontend/static/locales/en.json
index 628bde40..0dc2b326 100644
--- a/frontend/static/locales/en.json
+++ b/frontend/static/locales/en.json
@@ -1675,6 +1675,7 @@
},
"resource_list": {
"location": "Location",
- "wrong_drop_zone_msg": "Uploads only work in Files — open the Files section and drop there."
+ "wrong_drop_zone_msg": "Uploads only work in Files — open the Files section and drop there.",
+ "wrong_drop_zone_action": "Go to Files"
}
}
diff --git a/frontend/static/locales/es.json b/frontend/static/locales/es.json
index 19d0ddbd..79bb91a7 100644
--- a/frontend/static/locales/es.json
+++ b/frontend/static/locales/es.json
@@ -1294,6 +1294,7 @@
},
"resource_list": {
"location": "Ubicación",
- "wrong_drop_zone_msg": "Las subidas solo funcionan en Archivos — abre la sección Archivos y suelta ahí los elementos."
+ "wrong_drop_zone_msg": "Las subidas solo funcionan en Archivos — abre la sección Archivos y suelta ahí los elementos.",
+ "wrong_drop_zone_action": "Ir a Archivos"
}
}
diff --git a/frontend/static/locales/fa.json b/frontend/static/locales/fa.json
index 10db53d5..e6fb2d2b 100644
--- a/frontend/static/locales/fa.json
+++ b/frontend/static/locales/fa.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "مکان",
- "wrong_drop_zone_msg": "بارگذاری فقط در بخش پروندهها کار میکند — بخش پروندهها را باز کنید و آنجا رها کنید."
+ "wrong_drop_zone_msg": "بارگذاری فقط در بخش پروندهها کار میکند — بخش پروندهها را باز کنید و آنجا رها کنید.",
+ "wrong_drop_zone_action": "برو به پروندهها"
}
}
diff --git a/frontend/static/locales/fr.json b/frontend/static/locales/fr.json
index b2cfc6c9..eda28200 100644
--- a/frontend/static/locales/fr.json
+++ b/frontend/static/locales/fr.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "Emplacement",
- "wrong_drop_zone_msg": "Les envois ne fonctionnent que dans Fichiers — ouvrez la section Fichiers et déposez-y vos éléments."
+ "wrong_drop_zone_msg": "Les envois ne fonctionnent que dans Fichiers — ouvrez la section Fichiers et déposez-y vos éléments.",
+ "wrong_drop_zone_action": "Aller aux Fichiers"
}
}
diff --git a/frontend/static/locales/hi.json b/frontend/static/locales/hi.json
index 31a152ed..6823c303 100644
--- a/frontend/static/locales/hi.json
+++ b/frontend/static/locales/hi.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "स्थान",
- "wrong_drop_zone_msg": "अपलोड केवल फ़ाइलें अनुभाग में काम करता है — फ़ाइलें अनुभाग खोलें और वहीं छोड़ें।"
+ "wrong_drop_zone_msg": "अपलोड केवल फ़ाइलें अनुभाग में काम करता है — फ़ाइलें अनुभाग खोलें और वहीं छोड़ें।",
+ "wrong_drop_zone_action": "फ़ाइलों पर जाएँ"
}
}
diff --git a/frontend/static/locales/it.json b/frontend/static/locales/it.json
index 1ddd3942..3c6c1e78 100644
--- a/frontend/static/locales/it.json
+++ b/frontend/static/locales/it.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "Posizione",
- "wrong_drop_zone_msg": "I caricamenti funzionano solo in File — apri la sezione File e trascina lì gli elementi."
+ "wrong_drop_zone_msg": "I caricamenti funzionano solo in File — apri la sezione File e trascina lì gli elementi.",
+ "wrong_drop_zone_action": "Vai a File"
}
}
diff --git a/frontend/static/locales/ja.json b/frontend/static/locales/ja.json
index 5feff156..20347146 100644
--- a/frontend/static/locales/ja.json
+++ b/frontend/static/locales/ja.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "場所",
- "wrong_drop_zone_msg": "アップロードはファイル セクションでのみ機能します。ファイル セクションを開いてそこにドロップしてください。"
+ "wrong_drop_zone_msg": "アップロードはファイル セクションでのみ機能します。ファイル セクションを開いてそこにドロップしてください。",
+ "wrong_drop_zone_action": "ファイルへ移動"
}
}
diff --git a/frontend/static/locales/ko.json b/frontend/static/locales/ko.json
index 082c323b..754fae10 100644
--- a/frontend/static/locales/ko.json
+++ b/frontend/static/locales/ko.json
@@ -1671,6 +1671,7 @@
},
"resource_list": {
"location": "위치",
- "wrong_drop_zone_msg": "업로드는 파일 섹션에서만 작동합니다. 파일 섹션을 열고 거기에 놓아 주세요."
+ "wrong_drop_zone_msg": "업로드는 파일 섹션에서만 작동합니다. 파일 섹션을 열고 거기에 놓아 주세요.",
+ "wrong_drop_zone_action": "파일로 이동"
}
}
diff --git a/frontend/static/locales/nl.json b/frontend/static/locales/nl.json
index fc74afae..3f64e86f 100644
--- a/frontend/static/locales/nl.json
+++ b/frontend/static/locales/nl.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "Locatie",
- "wrong_drop_zone_msg": "Uploaden werkt alleen in Bestanden — open het onderdeel Bestanden en zet ze daar neer."
+ "wrong_drop_zone_msg": "Uploaden werkt alleen in Bestanden — open het onderdeel Bestanden en zet ze daar neer.",
+ "wrong_drop_zone_action": "Naar Bestanden"
}
}
diff --git a/frontend/static/locales/pl.json b/frontend/static/locales/pl.json
index f58b9426..02e8fada 100644
--- a/frontend/static/locales/pl.json
+++ b/frontend/static/locales/pl.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "Lokalizacja",
- "wrong_drop_zone_msg": "Przesyłanie działa tylko w Plikach — otwórz sekcję Pliki i upuść tam pliki."
+ "wrong_drop_zone_msg": "Przesyłanie działa tylko w Plikach — otwórz sekcję Pliki i upuść tam pliki.",
+ "wrong_drop_zone_action": "Przejdź do Plików"
}
}
diff --git a/frontend/static/locales/pt.json b/frontend/static/locales/pt.json
index 80dee965..0daf90ca 100644
--- a/frontend/static/locales/pt.json
+++ b/frontend/static/locales/pt.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "Localização",
- "wrong_drop_zone_msg": "Os envios só funcionam em Ficheiros — abra a secção Ficheiros e largue-os aí."
+ "wrong_drop_zone_msg": "Os envios só funcionam em Ficheiros — abra a secção Ficheiros e largue-os aí.",
+ "wrong_drop_zone_action": "Ir para Arquivos"
}
}
diff --git a/frontend/static/locales/ru.json b/frontend/static/locales/ru.json
index 71a83525..6921f2fd 100644
--- a/frontend/static/locales/ru.json
+++ b/frontend/static/locales/ru.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "Расположение",
- "wrong_drop_zone_msg": "Загрузки работают только в разделе Файлы — откройте раздел Файлы и перетащите туда."
+ "wrong_drop_zone_msg": "Загрузки работают только в разделе Файлы — откройте раздел Файлы и перетащите туда.",
+ "wrong_drop_zone_action": "Перейти к файлам"
}
}
diff --git a/frontend/static/locales/zh-TW.json b/frontend/static/locales/zh-TW.json
index c08b89ba..59e0cb1f 100644
--- a/frontend/static/locales/zh-TW.json
+++ b/frontend/static/locales/zh-TW.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "位置",
- "wrong_drop_zone_msg": "上傳僅在「檔案」中有效 — 請開啟檔案區並在該處拖放。"
+ "wrong_drop_zone_msg": "上傳僅在「檔案」中有效 — 請開啟檔案區並在該處拖放。",
+ "wrong_drop_zone_action": "前往檔案"
}
}
diff --git a/frontend/static/locales/zh.json b/frontend/static/locales/zh.json
index 61516be3..014c83e9 100644
--- a/frontend/static/locales/zh.json
+++ b/frontend/static/locales/zh.json
@@ -1279,6 +1279,7 @@
},
"resource_list": {
"location": "位置",
- "wrong_drop_zone_msg": "上传仅在 “文件” 中生效 — 请打开文件区并在那里拖放。"
+ "wrong_drop_zone_msg": "上传仅在 “文件” 中生效 — 请打开文件区并在那里拖放。",
+ "wrong_drop_zone_action": "前往文件"
}
}