Files
SwipeAnything/README.md
T
ilia ce43c5de6d
CI / Gitleaks (pull_request) Successful in 17s
CI / Unit tests (pull_request) Successful in 21s
Add organize folders, details sheet, and richer previews.
Map keys 0–9 to destinations, inspect files before deciding, and preview
PDF/RAW/ZIP/video with an OLED dark UI polish plus demo seed and tests.
2026-07-26 21:38:36 -04:00

184 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SwipeAnything
Unleash the swipe on anything. Point it at a folder or a photo library, and
(via adapters) at your inbox, a database table, or whatever else you need to
triage — one card at a time, right = keep, left = reject, like a dating app
for your backlog.
## Why
Tinder-style triage tools already exist, but each one is wired to a single
source: an image folder ([image-tinder](https://github.com/wsamuelw/image-tinder),
[photo-tinder-desktop](https://github.com/relaxis/photo-tinder-desktop)), an
inbox ([SwipeMail](https://github.com/RyanAJensen/SwipeMail)), or an AI
approval queue ([decision-desk](https://github.com/jdubb118/decision-desk)).
SwipeAnything separates the swipe UI from the source. An **adapter** turns
any collection of things into a queue of cards; the UI, keyboard shortcuts,
drag gestures, undo stack, and settings form all work the same regardless of
what's behind them. Write a new adapter and you get the whole UI for free.
## Status
Two adapters ship today — **local folder** and **Immich** (self-hosted photo
library) — proving the framework works for both a filesystem and a remote
API. Email, database-row, and other adapters are welcome as contributions;
see [CONTRIBUTING.md](CONTRIBUTING.md).
## Features
- Swipe (touch/mouse drag) or use the keyboard
- **Organize, not just triage:** map keys `0``9` to destination folders in
Settings, then press a number (or tap the chip) to move the current file
there. Undo brings it back.
- **Inspect before deciding:** press `i` for path, size, dates, and (on
macOS) image dimensions / camera metadata; Reveal in Finder + Copy path.
- Non-destructive by default: "reject" moves files to a `.swipeanything-trash/`
folder (or, for Immich, the library's own trash) — never a hard delete.
A separate, confirm-guarded "Empty trash" action is the only place that
permanently deletes anything.
- Undo, any number of steps back
- Session resume: closing the tab, reloading, or restarting the server picks
up right where you left off (as long as the underlying items haven't changed)
- Real thumbnails on macOS (via Quick Look), including HEIC/HEIF and video
poster frames, with automatic fallback to the original file everywhere else
- Native "Browse…" folder picker on macOS; type a path anywhere else
- Live progress + per-action counts
- Generic settings UI: every adapter declares its own config fields and gets
a form for free — no adapter-specific frontend code required
- Accessible by default: live region announcements per card, labeled action
buttons, a focus-trapped shortcuts dialog, visible focus rings, and a real
`role="progressbar"`
- Zero build step: Node.js + Express + vanilla HTML/CSS/JS
- `npm test` runs a real test suite (folder adapter, Immich adapter with a
mocked API, and the HTTP API end-to-end) — no separate test server to stand up
- CI on every push/PR (Gitea Actions): `npm test` + gitleaks
## Quickstart
```bash
git clone https://git.levkin.ca/ilia/SwipeAnything.git
cd SwipeAnything
npm install
npm start
```
Open `http://localhost:5757`. On first run you'll land on **Settings**
pick an adapter (the selected one is highlighted), fill in its settings
(Browse… opens a Finder dialog on macOS for folder paths), and start swiping.
Alternatively, copy `swipeanything.config.example.json` to
`swipeanything.config.json` and edit it directly:
```bash
cp swipeanything.config.example.json swipeanything.config.json
```
### Running the tests
```bash
npm test
```
Uses Node's built-in test runner (`node --test`) — no extra dev dependencies.
The Immich adapter is tested with a mocked `fetch`, so nothing here needs a
live server. The same suite is the PR gate in [`.gitea/workflows/ci.yml`](.gitea/workflows/ci.yml).
## Controls
| Action | Gesture | Key | Button |
|---|---|---|---|
| Keep (leave in place) | Drag right | `→` | Keep → |
| Reject (moves to trash) | Drag left | `←` | Reject ← |
| Skip / next (no decision) | — | `↓` or `Space` | Skip ↓ |
| Undo / go back | — | `↑` or `Ctrl/Cmd+Z` | Undo ↑ |
| Move to organize folder | — | `0``9` (if configured) | chips under the main buttons |
| File details (path, size, dimensions, …) | — | `i` | Details in header |
| Shortcuts help | — | `Shift+?` | `?` in header |
Keep/Reject (button or key) flash the KEEP/REJECT stamp and fling the card, same as a drag. Numbered organize actions flash the destination label and lift the card away. Details open as a bottom sheet over the card so the layout doesnt jump.
## Adapters
### Local folder
Point at any local folder. Optionally recurse into subfolders and filter by
extension. "Reject" moves the file into `.swipeanything-trash/` next to the
source; "Empty trash" (a separate, confirm-guarded action shown in the header
once there's anything to empty) permanently deletes what's in there.
In Settings, fill any of the **Organize folders (keys 09)** rows with a
destination path (and optional label). Only configured keys appear as
actions — leave a row blank to disable it. Destination folders are created
if missing, and if a destination sits inside the source tree it is skipped
when listing so sorted files don't reappear in the queue.
Press `i` (or **Details** in the header) to inspect the current file before
deciding: full path, size, dates, and on macOS image dimensions / camera /
capture date when Spotlight knows them. Details open as a bottom sheet over
the card. **Reveal in Finder** and **Copy path** are there too. Turn on
**Show file details on each card by default** in Settings if you always want
the sheet open.
### Immich
Point at a self-hosted [Immich](https://immich.app) server + API key and
swipe through your photo library newest-first (or randomly). "Reject" moves
the asset to Immich's own trash via the API; "Undo" restores it from there.
This is an early adapter — tested against the documented API shape, not
every Immich version, so please open an issue/PR if something doesn't match
your server.
## How adapters work
Every adapter implements a small contract (`adapters/base.js`): declare a
settings schema, list the items to review, apply/undo actions on them, and
stream a preview/thumbnail. `adapters/folder.js` (local filesystem) and
`adapters/immich.js` (remote API) are two different reference
implementations — read whichever is closer to what you're building, then see
[CONTRIBUTING.md](CONTRIBUTING.md) for a full walkthrough of writing your own
(email, database rows, RSS, anything).
```
UI (public/) --> Express API (server.js) --> Adapter Registry --> your adapter
```
## Project layout
```
server.js Express app: API + static file serving
adapters/base.js Adapter contract every adapter implements
adapters/folder.js Reference adapter: local files/folders
adapters/immich.js Reference adapter: remote API (Immich)
adapters/registry.js Adapter registration
lib/thumbnails.js macOS Quick Look-based thumbnail cache
public/ Vanilla HTML/CSS/JS swipe UI + settings UI
test/ node:test suite (adapters + HTTP API)
swipeanything.config.example.json Copy to swipeanything.config.json to configure
```
## Notes on platform-specific features
A few conveniences use macOS system tools instead of adding npm dependencies,
and quietly no-op elsewhere:
- **Thumbnails** use `qlmanage` (Quick Look). Falls back to serving the
original file if generation fails or times out, or on non-macOS platforms.
- **Folder picker** uses `osascript`/Finder. On other platforms, or if
automation permission is denied, just type the path into the field instead.
## Roadmap ideas
- Email adapter (IMAP): swipe archive/delete/label
- Generic JSON/CSV/database-row adapter: swipe to tag or update a status column
- Multi-select "later" bucket as a first-class third action everywhere
- Mobile-friendly PWA wrapper
## Changelog
See [CHANGELOG.md](CHANGELOG.md).
## License
MIT, see [LICENSE](LICENSE).