- Rust 42.1%
- Svelte 40.4%
- HTML 14.5%
- JavaScript 0.8%
- TypeScript 0.8%
- Other 1.4%
| .forgejo/workflows | ||
| .vscode | ||
| scripts | ||
| src | ||
| src-tauri | ||
| static | ||
| .gitignore | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| rust_gallery_project_outline.html | ||
| start.bat | ||
| svelte.config.js | ||
| tsconfig.json | ||
| vite.config.js | ||
Lens
A local image gallery for Windows and Linux with CLIP-powered reverse image search. No cloud, no Python runtime — everything runs on-device.
Features
- Browse & organise — add local folders; sub-folders are indexed automatically and shown in a collapsible tree; the folder sidebar is horizontally resizable by dragging its right edge
- Reverse image search — pick any image and find visually similar ones using CLIP embeddings (cosine similarity via usearch)
- Prompt & tag extraction — reads A1111/Forge
parametersfrom PNG tEXt chunks and JPEG EXIF UserComment; tags are searchable and filterable; generation parameters (model, steps, CFG, sampler, seed, etc.) parsed and shown in the lightbox - Live file watcher — new, modified, and deleted images are picked up automatically without a manual re-index
- Stop indexing — a Stop button appears in the progress bar during any index run; clicking it halts further processing while keeping everything already committed to the database (images, thumbnails, tags, and embeddings are all preserved)
- Folder path tracking — detects renamed or moved folders via the file watcher and a startup check; missing folders show a ⚠ badge and a Relocate… button to re-point the DB entry; the ↱ button on every folder lets you update its stored path at any time (useful for case-only renames that the OS does not report as a change)
- Lightbox — full-size view with EXIF metadata, AI prompt display, generation parameters (model, steps, CFG scale, sampler, seed), and a button to reveal the file in Explorer; click Copy next to any prompt to copy it to clipboard
- Sort & filter — sort by date indexed / filename / date taken; tag panel for click-based exact filtering; filename/tag filter bar with three modes:
- Regex — full regular-expression matching against the filename (e.g.
IMG_\d+,\.(png|webp)$); invalid patterns are highlighted in red - Word — case-insensitive substring search against the filename (e.g.
catmatchesMyCat.jpg); no special characters needed - Tag — case-insensitive substring search against tag names; comma-separate multiple terms to AND them together (e.g.
portrait, blonde hairreturns only images that have tags matching both terms) — mirrors AI generation prompt syntax
- Regex — full regular-expression matching against the filename (e.g.
- Delete images — remove images permanently from disk directly within the app:
- Bulk delete — click Select in the toolbar to enter selection mode, tick any number of thumbnails, then click Delete N; a confirmation dialog lists the count before anything is removed
- Single delete — open any image in the lightbox and click Delete image… in the metadata panel; an inline confirmation step prevents accidental deletion
- Light & dark themes — preference persisted to localStorage
- Grid density control — auto or fixed 1–12 columns, persisted to localStorage
Tech stack
| Layer | Technology |
|---|---|
| UI | SvelteKit 2 + Svelte 5 (TypeScript) |
| Desktop shell | Tauri 2 (Rust) |
| Database | SQLite via rusqlite with rusqlite_migration |
| CLIP inference | tract-onnx — pure Rust, no native DLL |
| Vector search | usearch — in-memory cosine index rebuilt from SQLite on startup |
| File watching | notify-debouncer-full with 500 ms debounce |
| Thumbnails | image crate, 320×320 JPEG to %APPDATA%/lens/thumbs/ |
| EXIF | kamadak-exif |
Prerequisites
All platforms
Linux only
sudo apt-get install \
libwebkit2gtk-4.1-dev \
libssl-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
build-essential \
patchelf
See the Tauri prerequisites guide for other distros.
CLIP model
Reverse image search requires clip_visual.onnx placed at src-tauri/models/clip_visual.onnx. The model is the CLIP ViT-B/32 visual encoder exported to ONNX (512-dimensional output). Without it the app runs normally but the "Similar" search button is hidden.
Development
# Install JS dependencies
npm install
# Start the Tauri dev server (hot-reloads the frontend; Rust recompiles on change)
npm run tauri -- dev
The first run compiles the Rust backend — this takes a few minutes. Subsequent runs are faster thanks to incremental compilation.
Building
# Produces an unsigned installer in src-tauri/target/release/bundle/
npm run tauri -- build
Outputs:
- Windows —
bundle/msi/Lens_x.y.z_x64_en-US.msiandbundle/nsis/Lens_x.y.z_x64-setup.exe - Linux —
bundle/appimage/lens_x.y.z_amd64.AppImageandbundle/deb/lens_x.y.z_amd64.deb
CI
A Forgejo Actions workflow (.forgejo/workflows/build.yml) builds Linux packages (.deb and .AppImage) on every v* tag push and on manual dispatch. Artifacts are uploaded to the workflow run.
git tag v0.1.0
git push origin v0.1.0
Data location
All app data is stored in the platform's standard application data directory — nothing is written next to the executable.
| Platform | Path |
|---|---|
| Windows | %APPDATA%\com.royp.lens\ |
| Linux | ~/.local/share/com.royp.lens/ |
Contents: lens.db (SQLite database) and thumbs/ (320×320 JPEG thumbnails).
