No description
  • TypeScript 56.5%
  • Rust 38.6%
  • JavaScript 2.9%
  • Shell 1.2%
  • CSS 0.7%
Find a file
dominic b5ff7f1f30
Some checks failed
Desktop builds / build (ubuntu-24.04) (push) Has been cancelled
Desktop builds / build (windows-latest) (push) Has been cancelled
Release 0.1.2 with duplicate review and resumed classification
2026-10-09 14:49:10 +02:00
.github/workflows Run desktop Rust tests in CI builds 2026-10-08 10:52:54 +02:00
docs Release 0.1.2 with duplicate review and resumed classification 2026-10-09 14:49:10 +02:00
results Stage signed updates for next launch and build GitHub releases 2026-10-05 20:29:51 +02:00
samples init 2026-10-04 12:40:21 +02:00
scripts Clarify development, build, launch, and install commands 2026-10-07 10:30:50 +02:00
src Release 0.1.2 with duplicate review and resumed classification 2026-10-09 14:49:10 +02:00
src-tauri Release 0.1.2 with duplicate review and resumed classification 2026-10-09 14:49:10 +02:00
tests Release 0.1.2 with duplicate review and resumed classification 2026-10-09 14:49:10 +02:00
.gitattributes Fix Windows release checks and bump version 2026-10-08 10:52:30 +02:00
.gitignore Remove obsolete Python experiment scripts and dependencies 2026-10-05 22:41:30 +02:00
.prettierrc.json init 2026-10-04 12:40:21 +02:00
AGENTS.md Clarify development, build, launch, and install commands 2026-10-07 10:30:50 +02:00
app-icon.svg init 2026-10-04 12:40:21 +02:00
components.json init 2026-10-04 12:40:21 +02:00
EXPERIMENT.md Remove obsolete Python experiment scripts and dependencies 2026-10-05 22:41:30 +02:00
index.html Rename application and repository checkout to Image Tag Manager 2026-10-05 19:43:46 +02:00
package.json Release 0.1.2 with duplicate review and resumed classification 2026-10-09 14:49:10 +02:00
playwright.config.ts Clarify development, build, launch, and install commands 2026-10-07 10:30:50 +02:00
pnpm-lock.yaml Add CPU thread options and classification timing history 2026-10-08 11:01:58 +02:00
README.md Release 0.1.2 with duplicate review and resumed classification 2026-10-09 14:49:10 +02:00
tsconfig.json init 2026-10-04 12:40:21 +02:00
vite.config.ts Remove obsolete Python experiment scripts and dependencies 2026-10-05 22:41:30 +02:00

Image Tag Manager

A local image library for Windows and Linux. Add multiple folders, let Qwen generate searchable tags and captions, and find images through a SQLite full-text index.

The application is React + TypeScript + Tailwind + shadcn-style Radix components, inside Tauri. Rust handles SQLite, folder scanning, thumbnails, model downloads, and llama.cpp. No Python is used to build or run the application. The original classifier evaluation report, sample images, and results remain in EXPERIMENT.md.

Install on Linux

Run pnpm install:linux from this checkout after installing the build prerequisites. It builds a release and installs Image Tag Manager for your current user, including the model runtime, icons, and an application-menu entry. No sudo is needed. To install an already-built release, use pnpm install:linux --no-build.

Launch Image Tag Manager from your application menu or run ~/.local/bin/image-tag-manager. The installer uses a user-writable AppImage, enabling in-app updates. The installed files live in ${XDG_DATA_HOME:-~/.local/share}/image-tag-manager-desktop; the desktop entry and icons use the same data directory. Your indexed library, settings, and downloaded models live in the separate ninja.domi.image-tag-manager data directory. On first launch, the app moves the previous com.domi.imageshelf directory to this location, preserving your library and models without copying them. Close the old app before launching the new version. The installed app does not depend on this checkout remaining in place.

Use

  1. Add folders using the plus button. Subfolders are included automatically. Enabled folders are rescanned at startup and every hour while the app is open, even with automatic classification turned off. A scheduled scan waits for any current task to finish; you can also use Scan folders at any time.
  2. Download the model once with Download model. It downloads 2.65 GB from Qwen's official repository, checks SHA-256, and resumes interrupted downloads.
  3. Choose Classify pending. If you close the app mid-batch, it resumes after the next launch and startup scan. Classify automatically is in Options; it checks the pending queue every 30 seconds while the app is open.
  4. Search tags, captions, filenames, and categories. Words use prefix matching and combine with AND. Click a tag to insert it into the search field as #tag. Type # followed by a name for autocomplete. Names with spaces use #"green trees"; selecting a suggestion adds the quotes for you. Hashtags match exact tags and combine with each other and ordinary search words. Clear the field to remove tag filters. Filter by folder or status.
  5. Open an image in a nearly full-window viewer. The image fits the available space without cropping; Hide details gives it the full width. Edit tags with autocomplete and click a tag button to remove it. Use Left/Right arrows or the previous/next buttons to browse the current search results. Arrow keys in text inputs keep editing the text. Leaving an image with unsaved changes asks before discarding them. The viewer preloads and decodes up to 10 results on either side. While a preview loads, the previous image stays visible beneath a spinner. The cache follows the current result and is released when the viewer closes; background loading is limited to two requests at a time. Scroll over the image to zoom toward the pointer, then drag to pan. Double-click toggles fit/2×; the image toolbar provides zoom and Fit controls. With the image focused, use + / - to zoom, 0 to fit, and Shift+arrows to pan. Zoom resets when changing images and is limited to 8× the fitted size. Trash or Delete moves the original file immediately to the system trash (Recycle Bin on Windows), without confirmation, then shows the next result or the previous result at the end. Unsaved edits are discarded when trashing. Delete in a text input edits text; holding Delete does not trash successive images. Restore files through your system trash, then rescan to index them again. Pale blue tags come from manual edits or folder names; hover for the exact source. AI tags stay neutral. Edits update search immediately.
  6. Use the settings button beside Tags to manage the shared tag catalog. Search, rename or merge tags, see image-use counts, filter unused tags, and purge orphans. Hide from search keeps a tag on its images but removes its name from text and hashtag search, the sidebar, search suggestions, and grid tag links. The image editor still shows it. Do not tag images removes existing assignments and blocks folder, AI, and manual assignments until unchecked. Tags with either setting stay in the catalog when unused tags are purged. Delete requires an unused tag with both settings unchecked. Purge never removes an image's assigned tags.
  7. Open Options to find exact file duplicates, change the CPU thread count, or check for updates. The duplicate list has a Trash button for each file. After classifying images at different settings, compare image counts, median times, and average times there. The thread setting applies to the next batch.

Resize thumbnails with Ctrl+scroll over the image grid or Ctrl+plus/minus. The footer has matching buttons and shows the width. Sizes range from 160 to 640 pixels and are remembered on this device. Ordinary scrolling and text size stay unchanged.

The grid shows edge-to-edge images without labels or icons. Hover over an image or focus it with the keyboard to show its filename, category, and clickable tags. These details are loaded with the grid and appear immediately. Pending thumbnails show a loading indicator until the image is ready to display; failed previews show an explicit message. Loaded tiles have no permanent decorations. Scroll continuously through the library. Only visible rows and two extra rows on each side stay mounted. Offscreen thumbnails and unused result pages leave the memory cache; scrolling back reloads them. Thumbnail decoding runs at most four requests at a time, and queued requests are canceled when their tiles leave view.

Each nested directory below a configured folder contributes tags from its name. Spaces split words; dots, dashes, and underscores stay in the tag. With a root of Photos, Photos/Alpine Meadows/scifi.clothes/image.jpg gets alpine, meadows, and scifi.clothes. The configured root and its parents do not become tags. Folder and manual tags survive AI classification and file-content changes. Removing a folder tag from an image suppresses it on later scans, even after orphan cleanup. Renaming a tag updates all its images as a user edit; naming an existing tag merges them. Files and folders on disk are not renamed.

Folder settings allow a display name, pause/resume, optional category list, and extra classification instructions. Categories and instructions apply to future classifications. Qwen still generates free-form tags when categories are supplied. Unknown categories become null. Tagging uses a schema-constrained JSON response.

Originals are never moved, renamed, or deleted. Removing a folder deletes only its index entries. Changed files return to the queue; files removed from a successfully scanned folder disappear from the index. An unavailable folder retains its entries and reports an error; disable it or restore access before running classification. Overlapping roots are rejected to avoid duplicate indexing. Symlinks are not followed.

Pause finishes the current image before stopping. Pending work survives restarts. Failed images remain visible with their errors and can be retried. Three consecutive inference failures stop the batch. Closing the app stops its native model process. Only one app instance is allowed.

Supported input: JPEG, PNG, WebP, GIF, BMP, TIFF. Animated formats use a still frame. Images over 100 MB or decoder safety limits produce an error. RAW and HEIC are not currently supported. EXIF orientation is respected. Preview thumbnails are cached.

Model and data

The app uses Qwen3-VL-2B-Instruct, Q8_0 text weights and the F16 vision projector. This is an 8-bit conversion of the previously tested Qwen model; it is not the original Transformers/BF16 runtime. The model revision and checksums are pinned in src-tauri/src/inference.rs. The native runtime is llama.cpp b11384, pinned in scripts/prepare-runtime.mjs.

Inference and prompt processing use 8 CPU threads by default. Change the count in Options. GPU offload is disabled, and images are processed one at a time. The model stays loaded between batches at the selected thread count. The local server binds to a random loopback port with a per-run API key; the web UI has no network model access. Only initial model/runtime downloads use the internet. Do not interpret generated tags as verified facts; edit questionable details.

Tauri's per-user application data directory stores:

  • index.sqlite3: folders, image metadata, tags, captions, categories, settings, classification timings, FTS5.
  • models/: downloaded GGUF weights and verification markers.
  • thumbnails/: cached JPEG previews.
  • llama-server.log: native inference diagnostics.

Typical Linux path: ~/.local/share/ninja.domi.image-tag-manager/. Typical Windows path: %APPDATA%\ninja.domi.image-tag-manager\. Tags are unique entities in tags, connected to images through image_tags. Each assignment records its source: AI, user, or folder. Repeated names are trimmed, lowercased, and deduplicated; use counts count distinct images. A tag can have several sources on one image. images.tags_text is a derived FTS cache of tags that are not hidden from search. Relationship triggers maintain it; tag assignments are the authoritative store.

Database changes use Refinery, with numbered SQL files embedded from src-tauri/migrations/. Applied versions, names, checksums, and timestamps live in refinery_schema_history. Pending migrations run together in one transaction. Failed upgrades roll back; changed migration checksums, missing migrations, and databases from newer app versions stop startup without resetting data.

The two pre-Refinery prototype schemas are discarded once, including folder settings and indexed classifications. Re-add folders and classify again. Image files and model weights are untouched. There is no string-tag conversion, legacy source, or automatic backup of these disposable prototype indexes. Unknown schemas are never reset.

To change the database, add the next V<number>__description.sql migration and run cargo test --manifest-path src-tauri/Cargo.toml --lib. Never edit, remove, or renumber an applied migration. See migration conventions.

The SQLite index uses WAL and foreign-key cascades. Search is paginated at 48 images; the UI never loads a whole large library into memory. Thumbnails are decoded serially to bound memory usage.

Developer docs

See architecture for the components and their data flow, and development for commands, builds, and releases.