diff --git a/TESTING.md b/TESTING.md
index 1b1d8f8..e1826a2 100644
--- a/TESTING.md
+++ b/TESTING.md
@@ -1376,6 +1376,32 @@ hätte keine Media-Query sie erreicht.
- [ ] Auf dem Flip 7 selbst gegenprüfen, auf beiden Schirmen
+## Bild ziehen lud es erneut hoch (24.08.2026)
+
+Zieht man das angezeigte Bild in der Vorschau ein Stück zur Seite, legt Chrome
+es als **echte Datei** in den Ziehvorgang. `dataTransfer.types` enthält dann
+`Files` – nicht zu unterscheiden von einer Datei aus dem Dateimanager. Die
+Ablagefläche lud das Bild daraufhin ein zweites Mal hoch.
+
+Nachgestellt und gemessen:
+
+| Fall | vorher | nachher |
+|---|---|---|
+| Vorschaubild ziehen und ablegen | `draggable=true`, **1 Upload** | `draggable=false`, **0 Uploads** |
+| Datei von außen ablegen | 1 Upload, Overlay sichtbar | unverändert 1 Upload, Overlay sichtbar |
+
+Zwei Riegel, weil einer allein zu leicht wieder aufgeht:
+
+1. `draggable={false}` an allen Bildern, die kein eigenes Ziehen brauchen –
+ Kachel-Vorschauen, Vorschaufenster, Suchtreffer, Papierkorb, Etiketten,
+ Logo, Profilbild. Die **Kachel** bleibt ziehbar und verschiebt die Datei.
+2. `hooks/useInternalDrag.ts`: Was in dieser Seite losgezogen ist, wird nie
+ als Upload behandelt. `dragstart` feuert nur bei eigener Quelle; von außen
+ gibt es keins.
+
+- [ ] Auf dem Handy gegenprüfen: Ein Bild lässt sich mit dem Finger nicht mehr
+ aus der Vorschau ziehen (dort war das Wischen ohnehin gemeint)
+
## Automatisiert geprüft (22.08.2026)
Diese Punkte hat Claude per API/`curl` gegen Wegwerf-Konten durchgespielt; sie
diff --git a/frontend/src/components/FileCover.tsx b/frontend/src/components/FileCover.tsx
index 36671ea..ca3a50c 100644
--- a/frontend/src/components/FileCover.tsx
+++ b/frontend/src/components/FileCover.tsx
@@ -22,6 +22,11 @@ export default function FileCover({ file }: { file: FileItem }) {
return (
<>
setFailed(true)}
diff --git a/frontend/src/components/FilePreview.tsx b/frontend/src/components/FilePreview.tsx
index 395f432..59559cf 100644
--- a/frontend/src/components/FilePreview.tsx
+++ b/frontend/src/components/FilePreview.tsx
@@ -280,6 +280,9 @@ export default function FilePreview({ file, onPrev, onNext, onClose, onSaved, se
{kind === 'image' && (
)}
diff --git a/frontend/src/components/SearchBox.tsx b/frontend/src/components/SearchBox.tsx
index 0a47ba0..e54953e 100644
--- a/frontend/src/components/SearchBox.tsx
+++ b/frontend/src/components/SearchBox.tsx
@@ -186,7 +186,7 @@ export default function SearchBox({ onOpenFolder, onOpenFile }: Props) {
{hit.kind === 'folder' ? (
) : hit.has_thumbnail ? (
-
) : (
diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx
index 744ab86..29cd424 100644
--- a/frontend/src/components/Sidebar.tsx
+++ b/frontend/src/components/Sidebar.tsx
@@ -379,6 +379,7 @@ export default function Sidebar({ user, view, activeTagId, onOpenTag,
{logoUrl ? (

boolean {
+ const active = useRef(false)
+
+ useEffect(() => {
+ const start = () => { active.current = true }
+ // Der Rückweg über zwei Ereignisse: `dragend` feuert an der Quelle,
+ // `drop` am Ziel. Fällt eines aus (abgebrochener Zug außerhalb des
+ // Fensters), setzt das andere zurück.
+ const stop = () => { active.current = false }
+ // Setzen in der Erfassungsphase, damit die Marke steht, bevor irgendein
+ // Handler sie liest. **Zurücksetzen dagegen in der Blasenphase**: Ein
+ // `drop`-Listener am Dokument mit `capture` liefe *vor* der Ablagefläche,
+ // hätte die Marke also schon gelöscht, wenn sie gebraucht wird.
+ document.addEventListener('dragstart', start, true)
+ document.addEventListener('dragend', stop)
+ document.addEventListener('drop', stop)
+ return () => {
+ document.removeEventListener('dragstart', start, true)
+ document.removeEventListener('dragend', stop)
+ document.removeEventListener('drop', stop)
+ }
+ }, [])
+
+ return () => active.current
+}
diff --git a/frontend/src/pages/FileBrowser.tsx b/frontend/src/pages/FileBrowser.tsx
index 6a0138c..9aac3d9 100644
--- a/frontend/src/pages/FileBrowser.tsx
+++ b/frontend/src/pages/FileBrowser.tsx
@@ -21,6 +21,7 @@ import MoveTo from '../components/MoveTo'
import TagDialog from '../components/TagDialog'
import OfficeEditor from '../components/OfficeEditor'
import { useAppSettings } from '../hooks/useAppSettings'
+import { useInternalDrag } from '../hooks/useInternalDrag'
import { DownloadIcon, ShareIcon, HistoryIcon, RenameIcon, MoveIcon, TrashIcon, FolderOpenIcon, StarIcon, CheckIcon, PeopleIcon, TagIcon, OfficeIcon, InfoIcon } from '../components/icons'
interface Props {
@@ -135,6 +136,7 @@ export default function FileBrowser({
const [ctxMenu, setCtxMenu] = useState
(null)
const [dialog, setDialog] = useState