From 539701fa87632caf281cc0789b86d454402ec4c7 Mon Sep 17 00:00:00 2001 From: Vladyslav Vladinov Date: Fri, 2 Oct 2026 19:53:34 +0200 Subject: [PATCH] chore(workspace): release 0.3.0 The browser build: `sqzer` on npm, staged from the tag. 0.3.0 and not 0.2.1 because `sqzer_codecs::png::PngEncoder` is removed. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 2 ++ CLAUDE.md | 2 +- Cargo.lock | 22 ++++++------ Cargo.toml | 18 +++++----- README.md | 23 ++++++++++-- ROADMAP.md | 9 +++-- tools/calibrate/Cargo.lock | 73 +++++++++++++++++++------------------- 7 files changed, 83 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9edf481..74281a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 0.3.0 + ### ⚠️ BREAKING CHANGES * **codecs:** `sqzer_codecs::png::PngEncoder`, the plain `png` writer, is removed. `sqzer_codecs::oxipng::OxipngEncoder` writes PNG on every target; effort 0 is its fastest setting diff --git a/CLAUDE.md b/CLAUDE.md index 1b83e6c..be85b10 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ Guidance for Claude Code working in this repository. Read `docs/adr/0001-system- ## What this is -`sqzer` is a multi-format image optimizer: a Rust library, a CLI and later a WASM package. The goal is Squoosh-quality defaults as a scriptable tool. Pure Rust by default, C codecs opt-in, AGPL codecs never in the library or default builds. +`sqzer` is a multi-format image optimizer: a Rust library, a CLI and a WASM package, `sqzer` on npm. The goal is Squoosh-quality defaults as a scriptable tool. Pure Rust by default, C codecs opt-in, AGPL codecs never in the library or default builds. Solo-maintained. Prefer fewer, well-chosen dependencies over coverage. Never write a decoder, encoder, resampler, colour transform or metric; mature crates exist for each and the job here is the container, the traits and the pipeline around them. diff --git a/Cargo.lock b/Cargo.lock index d5fbac9..58c37f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -845,14 +845,14 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "heif-dl" -version = "0.2.0" +version = "0.3.0" dependencies = [ "libloading", ] [[package]] name = "heif-imageio" -version = "0.2.0" +version = "0.3.0" dependencies = [ "objc2-core-foundation", "objc2-core-graphics", @@ -861,7 +861,7 @@ dependencies = [ [[package]] name = "heif-wic" -version = "0.2.0" +version = "0.3.0" dependencies = [ "windows", ] @@ -2175,7 +2175,7 @@ checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f" [[package]] name = "sqzer" -version = "0.2.0" +version = "0.3.0" dependencies = [ "bytemuck", "fast_image_resize", @@ -2188,7 +2188,7 @@ dependencies = [ [[package]] name = "sqzer-cli" -version = "0.2.0" +version = "0.3.0" dependencies = [ "anstream", "anstyle", @@ -2205,7 +2205,7 @@ dependencies = [ [[package]] name = "sqzer-codecs" -version = "0.2.0" +version = "0.3.0" dependencies = [ "avif-parse", "exr", @@ -2238,18 +2238,18 @@ dependencies = [ [[package]] name = "sqzer-codecs-agpl" -version = "0.2.0" +version = "0.3.0" dependencies = [ "sqzer-core", ] [[package]] name = "sqzer-core" -version = "0.2.0" +version = "0.3.0" [[package]] name = "sqzer-metrics" -version = "0.2.0" +version = "0.3.0" dependencies = [ "fast-ssim2", "sqzer-core", @@ -2257,7 +2257,7 @@ dependencies = [ [[package]] name = "sqzer-native-tier" -version = "0.2.0" +version = "0.3.0" dependencies = [ "sqzer-codecs", ] @@ -2287,7 +2287,7 @@ dependencies = [ [[package]] name = "sqzer-wasm" -version = "0.2.0" +version = "0.3.0" dependencies = [ "console_error_panic_hook", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 9d230ff..0472c3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = ["crates/*"] exclude = ["tools/calibrate"] [workspace.package] -version = "0.2.0" +version = "0.3.0" edition = "2024" # 1.92 is what `gamut-jxl` (the `native-jxl` backend) asks for; the # portable tier itself needs nothing newer than 1.89. @@ -21,19 +21,19 @@ keywords = ["image", "optimization", "avif", "jpegxl", "webp"] categories = ["multimedia::images", "command-line-utilities"] [workspace.dependencies] -sqzer-core = { path = "crates/sqzer-core", version = "0.2.0" } -sqzer-codecs = { path = "crates/sqzer-codecs", version = "0.2.0", default-features = false } -sqzer-metrics = { path = "crates/sqzer-metrics", version = "0.2.0" } -sqzer = { path = "crates/sqzer", version = "0.2.0", default-features = false } +sqzer-core = { path = "crates/sqzer-core", version = "0.3.0" } +sqzer-codecs = { path = "crates/sqzer-codecs", version = "0.3.0", default-features = false } +sqzer-metrics = { path = "crates/sqzer-metrics", version = "0.3.0" } +sqzer = { path = "crates/sqzer", version = "0.3.0", default-features = false } # The three crates that hold the workspace's `unsafe`, one per HEIC # decoder (ADR-0005 D4). Not public API; published because `sqzer-codecs` # depends on them. -heif-dl = { path = "crates/heif-dl", version = "0.2.0" } -heif-imageio = { path = "crates/heif-imageio", version = "0.2.0" } -heif-wic = { path = "crates/heif-wic", version = "0.2.0" } +heif-dl = { path = "crates/heif-dl", version = "0.3.0" } +heif-imageio = { path = "crates/heif-imageio", version = "0.3.0" } +heif-wic = { path = "crates/heif-wic", version = "0.3.0" } # The per-target selection behind `sqzer/native` (ADR-0006). Not public # API; published because `sqzer` depends on it. -sqzer-native-tier = { path = "crates/sqzer-native-tier", version = "0.2.0" } +sqzer-native-tier = { path = "crates/sqzer-native-tier", version = "0.3.0" } [workspace.lints.rust] unsafe_code = "forbid" diff --git a/README.md b/README.md index 1bfa8f2..c7d49b3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # sqzer -Multi-format image optimizer with best-in-class defaults. A library and a CLI, pure Rust by default, C codecs when you want the last few percent. +Multi-format image optimizer with best-in-class defaults. A library, a CLI and a WebAssembly package for the browser, pure Rust by default, C codecs when you want the last few percent. -> **Note**: Early days. The command line and its defaults are what this README describes; the library API will still change before `1.0`, and the browser build is not there yet. The design is in [`docs/adr/0001-system-design.md`](docs/adr/0001-system-design.md), the command line in [`docs/adr/0003-cli-interface.md`](docs/adr/0003-cli-interface.md), the native backends in [`docs/adr/0004-native-tier.md`](docs/adr/0004-native-tier.md), colour in [`docs/adr/0007-colour-management.md`](docs/adr/0007-colour-management.md). +> **Note**: Early days. The command line and its defaults are what this README describes; the library API will still change before `1.0`. The browser build is the `sqzer` package on npm, described [below](#in-the-browser); the page to drop an image on is next. The design is in [`docs/adr/0001-system-design.md`](docs/adr/0001-system-design.md), the command line in [`docs/adr/0003-cli-interface.md`](docs/adr/0003-cli-interface.md), the native backends in [`docs/adr/0004-native-tier.md`](docs/adr/0004-native-tier.md), colour in [`docs/adr/0007-colour-management.md`](docs/adr/0007-colour-management.md), the browser build in [`docs/adr/0011-browser-build.md`](docs/adr/0011-browser-build.md). ## What it is for @@ -161,6 +161,23 @@ rimage avif in.jpg sqzer -f avif in.jpg > **Note**: `rimage` scales an image up to the size asked for unless `--no-upscale` is given, which is why the lines above carry `--enlarge`. Most runs only mean to shrink, and for those `--enlarge` can go. +## In the browser + +The same pipeline and defaults as a WebAssembly package, `sqzer` on npm: the portable tier minus SVG, which the browser's canvas rasterises instead. + +```js +// npm install sqzer +import init, { optimize } from "sqzer"; +await init(); + +// AVIF for a photograph, lossless WebP for a graphic, searched to a score of 70 +const out = optimize(bytes); +// the command line's flags in camel case +const avif = optimize(bytes, { format: "avif", target: 60, width: 1600 }); +``` + +It is synchronous and single-threaded, so a page runs it in a Web Worker. The options, `decode` once and `encode` many times, `decodeAny` for SVG and HEIC, the result record and the errors are in [`crates/sqzer-wasm/README.md`](crates/sqzer-wasm/README.md), which is the package's README on npm. + ## Formats Decode: JPEG, PNG, WebP, AVIF, JPEG XL, GIF, TIFF, BMP, TGA, ICO, QOI, PNM, OpenEXR, SVG. HEIC behind a feature. OpenEXR is read as linear light in its own primaries, rotated to sRGB and encoded to 16-bit sRGB with anything above display white clipped; there is no tone mapping. @@ -205,7 +222,7 @@ crates/sqzer-codecs every backend behind a feature flag crates/sqzer-metrics SSIMULACRA2 and the target-quality search crates/sqzer library facade, the thing you depend on crates/sqzer-cli the binary, `sqzer` -crates/sqzer-wasm browser build: the JavaScript API, portable tier minus SVG. On npm with 0.3 +crates/sqzer-wasm browser build: the JavaScript API, portable tier minus SVG. `sqzer` on npm crates/sqzer-native-tier what `native` means per target, no code docs/adr design decisions dist-workspace.toml the release matrix for `cargo-dist` diff --git a/ROADMAP.md b/ROADMAP.md index 25107df..4ce7e60 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,12 +4,11 @@ What comes next for `sqzer`, roughly in order. `sqzer` is maintained by one pers Want to help with an item? Open an issue or a discussion first, see [`CONTRIBUTING.md`](CONTRIBUTING.md). -## 0.3: in the browser +## The page at `sqzer.dev` -The "Squoosh replacement" is not complete without a page you can drop an image on (ADR-0001 item 10). +The "Squoosh replacement" is not complete without a page you can drop an image on (ADR-0001 item 10). The package it runs on shipped with 0.3.0: the portable tier as WebAssembly, `sqzer` on npm ([`docs/adr/0011-browser-build.md`](docs/adr/0011-browser-build.md)). -- `sqzer-wasm`: the portable tier as a WebAssembly package, `sqzer` on npm. The API, the build and how it is published are [`docs/adr/0011-browser-build.md`](docs/adr/0011-browser-build.md). -- A small drag-and-drop page on top of it, at `sqzer.dev`. +- A small drag-and-drop page on top of that package, at `sqzer.dev`, in a repository of its own, `sqzer-dev/sqzer.dev`. > **Note**: The browser build inherits the portable tier's licence gaps: lossy WebP and JPEG XL cannot be written. Everything else the portable tier does, it does on `wasm32` too, AVIF decoding and `oxipng` included (ADR-0011). @@ -25,7 +24,7 @@ Palette reduction for PNG, `rimage`'s `--quantization`, as a codec option: ## A full build in the browser -After 0.3, a second browser build over `sqzer-codecs-agpl` that fills the portable tier's gaps with imazen's pure-Rust codecs: lossy WebP (`zenwebp`), JPEG XL encoding, AVIF decoding, HEIC input, and PNG quantisation through `imagequant`. It would power the drag-and-drop page at `sqzer.dev` and make it a full Squoosh replacement. +After the page, a second browser build over `sqzer-codecs-agpl` that fills the portable tier's gaps with imazen's pure-Rust codecs: lossy WebP (`zenwebp`), JPEG XL encoding, AVIF decoding, HEIC input, and PNG quantisation through `imagequant`. It would power the drag-and-drop page at `sqzer.dev` and make it a full Squoosh replacement. > **Note**: That build is AGPL-3.0, and so is anything that bundles it. It ships as its own package, never as the default `sqzer` package or in the library, which stay permissive. Whether those crates build for `wasm32` is not verified yet; that check and a new ADR come first. diff --git a/tools/calibrate/Cargo.lock b/tools/calibrate/Cargo.lock index 44805c6..7a7803c 100644 --- a/tools/calibrate/Cargo.lock +++ b/tools/calibrate/Cargo.lock @@ -950,14 +950,14 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "heif-dl" -version = "0.2.0" +version = "0.3.0" dependencies = [ "libloading", ] [[package]] name = "heif-imageio" -version = "0.2.0" +version = "0.3.0" dependencies = [ "objc2-core-foundation", "objc2-core-graphics", @@ -966,7 +966,7 @@ dependencies = [ [[package]] name = "heif-wic" -version = "0.2.0" +version = "0.3.0" dependencies = [ "windows", ] @@ -2007,29 +2007,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "re_rav1d" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5eb64c43c68024d96d99d52ef0525030d17bdcc6a6b4ddba048ec8f713c91fc" -dependencies = [ - "assert_matches", - "atomig", - "av-data", - "bitflags", - "cc", - "cfg-if", - "libc", - "nasm-rs", - "parking_lot", - "paste", - "raw-cpuid", - "static_assertions", - "strum", - "to_method", - "zerocopy 0.7.35", -] - [[package]] name = "read-fonts" version = "0.41.0" @@ -2273,7 +2250,7 @@ checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f" [[package]] name = "sqzer" -version = "0.2.0" +version = "0.3.0" dependencies = [ "bytemuck", "fast_image_resize", @@ -2297,7 +2274,7 @@ dependencies = [ [[package]] name = "sqzer-codecs" -version = "0.2.0" +version = "0.3.0" dependencies = [ "avif-parse", "exr", @@ -2318,10 +2295,10 @@ dependencies = [ "oxipng", "png", "ravif", - "re_rav1d", "resvg", "rgb", "sqzer-core", + "sqzer-rav1d", "tiff", "webpx", "yuv", @@ -2330,11 +2307,11 @@ dependencies = [ [[package]] name = "sqzer-core" -version = "0.2.0" +version = "0.3.0" [[package]] name = "sqzer-metrics" -version = "0.2.0" +version = "0.3.0" dependencies = [ "fast-ssim2", "sqzer-core", @@ -2342,11 +2319,34 @@ dependencies = [ [[package]] name = "sqzer-native-tier" -version = "0.2.0" +version = "0.3.0" dependencies = [ "sqzer-codecs", ] +[[package]] +name = "sqzer-rav1d" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "675eb2751acb31481c926e230087fd69ce497d5a6f8887ec5ba1c653c09b7cbb" +dependencies = [ + "assert_matches", + "atomig", + "av-data", + "bitflags", + "cc", + "cfg-if", + "libc", + "nasm-rs", + "parking_lot", + "paste", + "raw-cpuid", + "static_assertions", + "strum", + "to_method", + "zerocopy 0.7.35", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -2376,23 +2376,22 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", "syn 2.0.119", ]