Skip to content

Repository files navigation

Image Compressor

A standalone Windows + macOS desktop app that compresses and resizes images to hit a target file size (a KB or MB cap) — one image or a whole batch. Fast, fully offline, no network calls. Built with Tauri 2; the compression engine is pure Rust.

"Get this under 500 KB." "Get all of these under 2 MB."

Image Compressor

Features

  • Compress a single image or a whole batch (drag-and-drop files/folders, or the native picker; folders are scanned recursively)
  • Target file size with a KB / MB toggle
  • Two resize modes: Fit (preserve aspect ratio, optional longest-edge cap) or Exact — crop-to-fill to an exact width × height (centre/start/end anchor, no borders); in Exact mode dimensions are locked so only quality varies
  • Per-file cap overrides, or a total-folder budget mode that fits a whole set under one combined cap (split across images by size)
  • Input: JPEG, PNG, WebP, TIFF. Output: JPEG (default), PNG (lossless, oxipng-optimized), or AVIF (best ratio, slower), plus "keep original"
  • Live before/after preview — click any image to preview it; the size/quality readout recomputes instantly as you change settings (the decoded source is cached, so tweaks are fast)
  • Thumbnails in the file list, with a selectable preview target
  • Transparency handling: PNG/AVIF keep alpha; JPEG flattens onto a configurable background color
  • Parallel batch processing (Rust rayon, capped to the core count) with overall + per-file progress
  • Cancel mid-run with accurate partial results
  • Per-file isolation: a corrupt or unreachable file is recorded with a reason and the batch continues — it never aborts the job or panics
  • Result summary per file: original → final size, % saved, final quality/dimensions, and any failures
  • Presets & delivery profiles — save the current recipe as a named preset, or pick a built-in one (MLS listing, Web hero AVIF, Client gallery budget)
  • Bulk rename with tokens ({name} {seq:000} {date} {w} {h}) and a live preview
  • Metadata control (strip all / keep ICC / no GPS) and optional sRGB conversion; EXIF orientation is always baked into the pixels
  • Optional perceptual quality floor (won't ship below a target SSIM — trades resolution to stay sharp) and a SSIM / PSNR readout in the preview
  • Watch folder — auto-compress every image dropped into a folder with the current preset, writing to a separate output folder (never re-ingests its own output)
  • Command line (imgc) and OS right-click integration — see CLI & shell integration
  • Built-in help panel, light / dark themes, keyboard-operable, AA contrast

Installing the downloaded app (first launch)

The released installers are not code-signed or notarized, so the first time you open the app, macOS Gatekeeper (or Windows SmartScreen) shows a warning. This is expected for an unsigned build downloaded from the internet — it doesn't indicate a problem with the app, and it's a one-time step per install.

macOS — this only happens for the downloaded app; a build you compile locally is never quarantined. After dragging the app into Applications, either:

  • Terminal: run xattr -dr com.apple.quarantine "/Applications/Image Compressor.app", then open it normally; or
  • System Settings: open the app once (the warning appears), then go to System Settings → Privacy & Security, scroll to the Security section, and click Open Anyway next to the Image Compressor message, then confirm. (On macOS 15+ the old right-click → Open shortcut no longer bypasses this.)

Windows — if SmartScreen shows "Windows protected your PC", click More info → Run anyway.

To remove the warning entirely, the maintainer can enable code signing + notarization — see Building installers.

How the target-size algorithm works

Each image is decoded once and (optionally) downscaled to a max long edge. The encoder quality is then binary-searched for the largest file that still fits the cap. If even the lowest quality is over the cap, the dimensions are downscaled (by a factor derived from the size overshoot, clamped) and the search retries. If the longest edge falls below a 16 px floor and the cap still can't be met, the file is marked unreachable. All search happens in memory: each source is decoded once and written once.

When a cap is reachable the output is always ≤ the cap, and the search returns the best quality under the cap (one quality step higher would exceed it). Rust tests assert exactly this.

In Exact mode, crop-to-fill computes the crop in source space then resizes once; dimensions are locked so the quality search alone meets the cap, and an unmeetable cap at the exact size is reported unreachable (never silently shrunk).

Command line & right-click integration

A headless CLI, imgc, ships alongside the app for scripting and CI. It lives in its own workspace crate (src-tauri/crates/cli) that depends on the engine crate only — never on Tauri — so the GUI and CLI share one compression implementation:

cd src-tauri && cargo build --release -p imgc
imgc compress ~/Photos --cap 800k --format jpeg --max-dimension 2048
imgc shell install        # adds the "Compress with Image Compressor" right-click menu

imgc shell {install,uninstall,print} wires up a macOS Finder Quick Action or Windows Explorer context-menu entry that hands the selected images to imgc compress. Full flag reference, the macOS manual-setup fallback, and the Windows .reg details are in docs/cli-and-shell-integration.md.

Tech stack

  • Tauri 2 — Rust core; WebView2 on Windows, WKWebView on macOS
  • React 18 + TypeScript (strict) + Vite frontend, Tailwind CSS, Zustand state
  • Tauri plugins: dialog, fs, store
  • Engine crates: image (decode/encode), ravif (AVIF), oxipng (lossless PNG optimization), fast_image_resize (Lanczos3 resize), rayon, thiserror, serde

The compression engine is a separate workspace crate (src-tauri/crates/engine) with no Tauri dependency, so it is platform-neutral and unit-testable on its own.

Project structure

.
├── src/                       # React + TypeScript frontend
│   ├── components/            # Intake, CapControls, PresetBar, Settings, RunBar, SummaryBanner …
│   ├── lib/                   # tauri bridge, types, format helpers, icons, theme
│   └── store/                 # Zustand store
├── src-tauri/                 # Tauri app
│   ├── src/                   # commands.rs, watcher.rs, lib.rs (thin bridge to the engine)
│   ├── crates/engine/         # pure-Rust target-size engine (no Tauri deps)
│   ├── crates/cli/            # `imgc` headless CLI + right-click integration (engine-only)
│   ├── capabilities/          # scoped permissions
│   └── tauri.conf.json
├── design/                    # original brief: spec, roadmap, build-agent prompt
├── docs/                      # README assets (screenshot)
├── .github/workflows/         # release.yml (dual-OS installers) + ci.yml (verify gate)
└── package.json

The original design brief — the algorithm spec, the phase roadmap (with status), and the build-agent prompt — lives in design/. This README reflects the app as it ships today.

Prerequisites

  • Node.js 18+ and npm
  • The Rust toolchain (rustup / cargo)
  • Windows: Microsoft C++ Build Tools (MSVC) and the WebView2 runtime (usually preinstalled on Win 10/11)
  • macOS: Xcode command-line tools (xcode-select --install)

Development

npm install
npm run tauri dev     # launch the desktop app with hot reload
npm run dev           # frontend only, in a browser (no Rust backend)
npm run tauri build   # build an installer for the current OS

Quality checks

npm run verify            # typecheck + ESLint (0 warnings) + Vitest + cargo fmt/clippy/test
npm run rust:test-engine  # run just the engine tests (fast)

Enforced hard rules: no any / @ts-ignore / eslint-disable; no Rust unwrap/expect/panic! outside tests; no OS-specific branching in the engine.

Building installers (Windows + macOS)

You cannot build both installers on one machine — each OS's bundle must be built on that OS. A GitHub Actions release workflow (.github/workflows/release.yml) uses a matrix of windows-latest and macos-latest with tauri-action to build both from one version tag:

  • Windows → .msi (WiX) and .exe (NSIS)
  • macOS → universal .dmg / .app (Apple Silicon + Intel)

Push a tag like v0.4.0 to trigger it; the workflow creates a draft GitHub Release and attaches the installers. Optional code signing — Windows Authenticode, macOS Developer ID + notarization — is left to the maintainer.

Encoder backend (a deliberate decision)

The app uses pure-Rust encoders so it builds cleanly on both Windows and macOS with no C toolchain: the image crate for JPEG (quality search) and PNG (lossless), and ravif for AVIF (best ratio, slower to encode). Lossy WebP is the one format still deferred — it needs a native library (libwebp) that adds build friction — rather than forced. This follows the spec's library-risk guidance.

License

MIT

About

Target-size image compressor — a Tauri 2 + Rust desktop app that compresses and resizes images to hit a KB/MB file-size cap, single or batch. Offline, cross-platform.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages