- HTML 38%
- Shell 34.2%
- JavaScript 27.3%
- Nix 0.5%
| renderer | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| cliff.toml | ||
| flipdrop.sh | ||
| LICENSE | ||
| main.js | ||
| package.json | ||
| pemmikan.md | ||
| preload.js | ||
| README.md | ||
| shell.nix | ||
FlipDrop
Kleines Floating-Fenster als Drag-and-Drop-Quelle für Screenshots — Verzeichnisinhalt nach ctime rückwärts sortiert, mit großer Bildvorschau.
Zweck
Ein immer-obenauf-Fenster (X11/i3), das ein Verzeichnis nach ctime absteigend
zeigt — also danach, wann die Datei dort aufgetaucht ist, nicht nach mtime. Das ist
der Unterschied, auf den es ankommt: bei Kopien aus Nextcloud oder aus dem SyncDir
bleibt die mtime die des Originals, die ctime ist der Moment des Ankommens. Sortiert
nach mtime landet der frische Screenshot irgendwo in der Mitte, sortiert nach ctime
ganz oben.
Gedacht als Drag-and-Drop-Quelle, um Screenshots schnell nach Claude zu schieben — in VSCodium, in den Browser oder in ein Terminal.
Features
- Immer-obenauf-Fenster (X11/i3), klein und aus dem Weg
- Listet ein Verzeichnis (Default
~/SyncDir/Screenshots) nach ctime absteigend - Große Bildvorschau statt Dateinamen-Raten
- Datei per Drag-and-Drop (XDND) nach außen ziehen — VSCodium, Browser, Terminal
- Klick kopiert den Dateipfad ins Clipboard (CLIPBOARD und PRIMARY)
- Aktualisiert sich automatisch per
fs.watch, zeigt maximal 200 Einträge
Aufruf
./flipdrop.sh # Default-Verzeichnis, auf conkys Spur
./flipdrop.sh -d ~/Bilder # anderes Verzeichnis
./flipdrop.sh --dir=~/Bilder # gleichbedeutend
./flipdrop.sh -l # linke Kante
./flipdrop.sh -r # rechte Kante (Default)
./flipdrop.sh -c # auf conkys Spur
./flipdrop.sh -w 420 # feste Breite in Pixeln
./flipdrop.sh --help # Kurzhilfe
Flags:
| Flag | Wirkung |
|---|---|
-d, --dir VERZEICHNIS, --dir=VERZEICHNIS |
Verzeichnis, das gezeigt wird |
-l, --left |
Fenster an die linke Kante des Ankerbereichs |
-r, --right |
Fenster an die rechte Kante (Default) |
-c, --conky |
Anker conky — exakt auf conkys Spur (Default) |
-m, --monitor |
Anker monitor — 380 px breiter Streifen am Bildschirmrand |
-w, --width PX |
Fensterbreite in Pixeln |
-h, --help |
Kurzhilfe |
Environment-Variablen:
| Variable | Default | Bedeutung |
|---|---|---|
FLIPDROP_DIR |
~/SyncDir/Screenshots |
Verzeichnis, das gezeigt wird |
FLIPDROP_SIDE |
right |
Seite: right, left oder center |
FLIPDROP_ANCHOR |
conky (Default) |
Bezugsrechteck: conky, monitor oder workspace |
FLIPDROP_WIDTH |
conkys Breite | Fensterbreite; bei Anker monitor ohne Angabe 380 |
FLIPDROP_MARGIN |
6 |
Rand in Pixeln zu den Kanten des Ankerbereichs |
FLIPDROP_DEBUG |
leer | gesetzt: berechnete Geometrie auf stderr ausgeben |
FLIPDROP_GEOM |
— | übersteuert die Größe, Format "Breite Höhe" |
FLIPDROP_POS |
— | übersteuert die Position, Format "X Y" |
Flags gewinnen gegen die gleichnamigen Variablen; FLIPDROP_GEOM/FLIPDROP_POS
übersteuern am Ende alles.
Positionierung
Vertikal richtet sich FlipDrop immer am i3-Workspace aus. i3 kennt den
nutzbaren Bereich exakt — Polybar und andere Struts sind im Workspace-Rechteck
bereits abgezogen, das Fenster verschwindet also nicht unter der Leiste. Fehlt
i3 (oder jq), dient das Monitor-Rechteck aus xrandr als Ersatz.
Horizontal entscheidet der Anker, welches Rechteck gilt:
| Anker | Bezug |
|---|---|
conky (Default) |
exakt conkys Spur — Position und Breite werden aus den conky-Fenstern gemessen |
monitor |
ganzer Bildschirm — ignoriert, was conky für sich reserviert |
workspace |
der i3-Bereich; bei holm endet der vor conky |
Für den Anker conky sucht der Launcher per xdotool alle Fenster der Klasse
conky und bildet deren Hüllrechteck — conky läuft oft als mehrere Fenster
übereinander in derselben Spalte; Fenster mit weniger als 400 px² Fläche gelten
als Hilfsfenster und werden übersprungen. Findet sich kein conky-Fenster, fällt
der Launcher mit einer Meldung auf den Anker monitor zurück.
conkys Spur wird bei jedem Start neu gemessen und nie fest verdrahtet: conky sitzt auch bei anderen Auflösungen am Bildschirmrand, und die gemessene Position folgt ihm dorthin.
Innerhalb des Ankerbereichs setzt FLIPDROP_SIDE das Fenster an die rechte
Kante (Default), an die linke oder mittig; FLIPDROP_MARGIN hält den Abstand
zu den Kanten.
Der Launcher rechnet die Geometrie fertig aus und übergibt sie der App als
--geom=BxH+X+Y; die App setzt sie beim Erzeugen des Fensters selbst. Mit
gesetztem FLIPDROP_DEBUG schreibt der Launcher Ankerbereich und Ergebnis nach
stderr:
flipdrop: anker=conky bereich=380x1052@1534,28 -> fenster 380x1040@1534,34
Bedienung
| Aktion | Wirkung |
|---|---|
| Klick | Pfad ins Clipboard (CLIPBOARD und PRIMARY) |
| Doppelklick | Datei öffnen |
| Ziehen | Datei per XDND ans Ziel übergeben |
+ / - |
Spaltenzahl 1–4 (wird in localStorage gemerkt) |
r / F5 |
Liste neu laden |
q / Esc |
Fenster schließen |
| Titelleiste ziehen | Fenster verschieben |
Voraussetzungen
- Linux mit X11 (Wayland ist ungetestet)
- Electron — über
shell.nixaus nixpkgs, getestet mit 41.9.1 xrandrundsed— Monitor-Rechtecki3-msgundjq— Workspace-Rechteck unter i3; fehlt beides, gilt der Monitorxdotool— nur für den Ankerconky, zum Messen der conky-Fenster
Kein Perl, kein xclip — das Clipboard bedient Electron selbst.
Installation
git clone ssh://git@forgejo.mueller.network:2222/holm.tools.public/FlipDrop.git
cd FlipDrop
./flipdrop.sh
Symlink in den Pfad:
ln -s "$PWD/flipdrop.sh" ~/bin/flipdrop
FlipDrop steht in der distribution/registry.list von toolctl und lässt sich
darüber wie die übrigen Tools ausrollen; bei holm existiert der Symlink
~/bin/flipdrop bereits.
i3-Regel
i3 legt das Fenster per Default tiled an. Mit dieser Regel in der i3-Config sitzt es sofort richtig:
for_window [class="(?i)flipdrop"] floating enable, sticky enable, border pixel 2, resize set 380 900, move position 60 60
Größe und Position kommen inzwischen vom Launcher (--geom=…); nötig ist an der
Regel also nur noch floating enable, dazu nach Geschmack sticky/border —
resize set und move position würden die berechnete Geometrie übersteuern.
Stolperfallen
ELECTRON_RUN_AS_NODE
Aus einem VSCodium-Terminal gestartet erbt der Prozess ELECTRON_RUN_AS_NODE=1.
Electron läuft dann als reines Node und stirbt sofort mit
Cannot find module 'electron' — was nach einem kaputten Setup aussieht, aber keins
ist.
flipdrop.sh räumt diese Variable und ihre VSCODE_*-Geschwister vor dem Start ab.
Wer stattdessen direkt electron . aufruft, muss das selbst tun:
unset ELECTRON_RUN_AS_NODE ELECTRON_NO_ATTACH_CONSOLE NODE_OPTIONS
unset VSCODE_ESM_ENTRYPOINT VSCODE_CODE_CACHE_PATH VSCODE_NLS_CONFIG VSCODE_CWD
electron .
Symlink-Pfad: readlink -f
Über ~/bin/flipdrop gestartet zeigt BASH_SOURCE auf den Symlink, nicht ins
Repo — ohne Auflösung sucht der Launcher shell.nix in ~/bin und findet
nichts. flipdrop.sh löst den eigenen Pfad deshalb mit readlink -f auf, bevor
es das App-Verzeichnis bestimmt.
xdotool search und set -e
xdotool search endet mit Exit 1, wenn es nichts findet. In einer Zuweisung
VAR="$(xdotool search …)" ist dieser Status der des ganzen Kommandos — unter
set -e beendet ein völlig normaler Fall („kein conky da") also das Script.
Deshalb steht dort || true.
Warum die Geometrie nicht per i3-msg nachgesetzt wird
Naheliegend wäre, das Fenster nach dem Start per i3-msg an seinen Platz zu
schieben. Das setzt aber voraus, die Fenster-ID zu erraten, und daran hängen
zwei Probleme: X11 recycelt IDs — ein frisches Fenster kann die des eben
geschlossenen erben — und Electron legt zusätzlich Hilfsfenster derselben
WM-Klasse an (10x10 und 200x200). Der Befehl landet dann auf dem falschen
Fenster. Stattdessen berechnet der Launcher die Geometrie und gibt sie als
--geom=BxH+X+Y mit; die App setzt sie beim Erzeugen des Fensters.
Bekannte Einschränkung: Drop nach Alacritty
Ein Drop in ein Alacritty-Fenster bewirkt nichts — kein Fehler, keine Reaktion. Das ist ein Upstream-Bug und liegt nicht an FlipDrop:
| Test | Ergebnis |
|---|---|
FlipDrop → xdragon --target |
funktioniert, text/uri-list kommt korrekt an |
xdragon → Alacritty |
funktioniert |
| FlipDrop → Alacritty | schlägt fehl |
Ursache: winit antwortet in XdndStatus/XdndFinished mit dem Action-Atom
XdndActionPrivate. Chromiums AtomToDragOperation() kennt nur Copy/Move/Link
und liefert für alles andere kNone; HandleMouseReleased() sendet XdndDrop
nur bei != kNone. Chromium bricht den Drag deshalb still ab, das Ziel bekommt
nie ein Drop-Event.
Auf winit master ist das behoben (Commit 156433e, auf XdndActionCopy
geändert), aber in keinem Release enthalten. Alacritty v0.17.0 hängt an
winit 0.30.9 und ist betroffen. Quellseitig ist nichts zu machen: startDrag
reicht nur {file, icon} durch, und die MIME-Typen sind ohnehin korrekt —
die Bruchstelle ist das Action-Atom, nicht der Typ.
Bis ein winit-Release mit dem Fix erscheint: Drop-Ziele außerhalb von Alacritty verwenden (VSCodium und Browser funktionieren), oder als Behelf die Kachel anklicken — der Pfad liegt dann in CLIPBOARD und PRIMARY.
Changelog
Siehe CHANGELOG.md. Wird per git-cliff aus Conventional Commits generiert. Manuell nicht editieren — stattdessen Commit-Messages sauber halten:
feat(scope): …→ Addedfix(scope): …→ Fixedperf(scope): …→ Performancerefactor(scope): …→ ChangedBREAKING CHANGE: …im Body → Major-Bump
Lizenz
MIT — Copyright (c) 2026 holm / Christian Müller, unter Zuhilfenahme von Claude.ai (Anthropic).
Siehe LICENSE.