diff --git a/frontend/src/routes/search/+page.svelte b/frontend/src/routes/search/+page.svelte
index 0d01f6ad..5cee5e4a 100644
--- a/frontend/src/routes/search/+page.svelte
+++ b/frontend/src/routes/search/+page.svelte
@@ -11,6 +11,7 @@
import Icon from '$lib/icons/Icon.svelte';
import { t } from '$lib/i18n/index.svelte';
import { files as filesStore } from '$lib/stores/files.svelte';
+ import { ui } from '$lib/stores/ui.svelte';
import { formatBytes } from '$lib/utils/format';
import { formatDate, iconNameFromClass, fileIconKindClass } from '$lib/utils/display';
@@ -202,10 +203,61 @@
void dateFilter;
void run(query);
});
+
+ /**
+ * Page-wide OS drag/drop guard.
+ *
+ * `/search` has no upload path, but without capturing the drop the
+ * browser's default handler navigates to the dropped file — the tab
+ * REPLACES the app with the file itself, which is data-loss-esque
+ * (user loses their in-progress search + any unsaved UI state).
+ * The `ResourceList`-based views (`/photos`, `/shared`, `/trash`, …)
+ * already fire a "wrong drop zone" toast via their `.rl-root`
+ * wrapper when `enableSystemDrop` is false — this handler brings
+ * `/search` to the same contract, and covers the whole viewport
+ * (not just a list surface) so drops on the sticky header /
+ * result-card margins are caught too. Same toast copy + "Go to
+ * Files" action as `ResourceList` for a consistent recovery UX.
+ */
+ function onWindowDragOver(e: DragEvent) {
+ if (!e.dataTransfer?.types?.includes('Files')) return;
+ if (e.defaultPrevented) return;
+ e.preventDefault();
+ // `dropEffect = 'none'` would tell the browser to REJECT the
+ // drop before `drop` fires — the toast path below never runs.
+ // Accept at the pointer level and let `onWindowDrop` decide.
+ if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
+ }
+ function onWindowDrop(e: DragEvent) {
+ if (!e.dataTransfer?.types?.includes('Files')) return;
+ if (e.defaultPrevented) return;
+ e.preventDefault();
+ ui.notify(
+ t(
+ 'resource_list.wrong_drop_zone_msg',
+ 'Uploads only work in Files — open the Files section and drop there.'
+ ),
+ 'warning',
+ 6000,
+ true,
+ {
+ action: {
+ label: t('resource_list.wrong_drop_zone_action', 'Go to Files'),
+ // Best-effort recovery — the drop's `DataTransfer` is
+ // discarded once this handler returns (browsers don't
+ // persist it across a navigation), so we land the
+ // user in `/files` where they can re-drag from the OS.
+ onClick: () => goto(resolve('/files'))
+ }
+ }
+ );
+ }