diff --git a/web/src/lib/components/DropOverlay.svelte b/web/src/lib/components/DropOverlay.svelte
new file mode 100644
index 0000000..c146ec5
--- /dev/null
+++ b/web/src/lib/components/DropOverlay.svelte
@@ -0,0 +1,96 @@
+
+
+
+ {@render children()}
+ {#if dragging}
+
+ {/if}
+
+
+
diff --git a/web/src/lib/components/DropZone.svelte b/web/src/lib/components/DropZone.svelte
index b11a757..6ba835d 100644
--- a/web/src/lib/components/DropZone.svelte
+++ b/web/src/lib/components/DropZone.svelte
@@ -11,7 +11,24 @@
$props();
let input = $state();
- let dragging = $state(false);
+ let depth = $state(0);
+ const dragging = $derived(depth > 0);
+
+ function enter() {
+ depth += 1;
+ }
+
+ function leave() {
+ depth = Math.max(0, depth - 1);
+ }
+
+ function over(event: DragEvent) {
+ event.preventDefault();
+ }
+
+ function end() {
+ depth = 0;
+ }
function accept(file: File | undefined | null) {
if (!file) return;
@@ -40,14 +57,13 @@
openPicker();
}
}}
- ondragover={(e) => {
- e.preventDefault();
- dragging = true;
- }}
- ondragleave={() => (dragging = false)}
+ ondragenter={enter}
+ ondragleave={leave}
+ ondragover={over}
+ ondragend={end}
ondrop={(e) => {
e.preventDefault();
- dragging = false;
+ depth = 0;
accept(e.dataTransfer?.files[0]);
}}
>
diff --git a/web/src/lib/components/ToolPage.svelte b/web/src/lib/components/ToolPage.svelte
index ae65c93..63757d3 100644
--- a/web/src/lib/components/ToolPage.svelte
+++ b/web/src/lib/components/ToolPage.svelte
@@ -5,6 +5,7 @@
import { defaultParams, outputOf, sanitizeParams, type ToolEntry } from '$lib/registry';
import Button from './ui/Button.svelte';
import DownloadButton from './DownloadButton.svelte';
+ import DropOverlay from './DropOverlay.svelte';
import DropZone from './DropZone.svelte';
import EmptyState from './ui/EmptyState.svelte';
import InfoPanel from './InfoPanel.svelte';
@@ -109,10 +110,12 @@
{#if !source}
(errorText = message)} />
{:else}
-
-
+ (errorText = message)}>
+
+
+
{/if}