The edit-metadata integration tests require exiftool (libimage-exiftool-perl)
which was missing from the CI test runner, causing 4 tests to fail with 422.
Update erase-object pipeline, eraser canvas, and inpainting Python script.
Add LaMa model download script and update Dockerfile for model support.
Update multi-file tool routes for consistency.
Replace the confusing 2-mode smart crop with a clear 3-mode system:
- Subject Focus: Sharp attention/entropy saliency crop with social media presets
- Face Focus: MediaPipe face detection with headshot framing presets
- Auto Trim: Border removal with optional pad-to-square
Adds detectFaces() to AI package, face preset constants, backward
compatibility for old mode names, and comprehensive integration tests.
- Pin PaddlePaddle to 3.0.0 on ARM64 to fix segfault in PIR inference
engine (3.1+ crashes on aarch64 Debian Bookworm)
- Fix text extraction for PaddleOCR 3.4.x result format (rec_texts)
- Add Node.js-level fallback chain (best -> balanced -> fast) when
Python subprocess crashes
- Add multi-image OCR: processes all uploaded files sequentially with
per-file progress and filename headers in combined output
- Convert input images to PNG via Sharp before OCR so HEIC, AVIF, WebP,
TIFF all work transparently
- Implement real auto-detect language using Tesseract multi-lang script
detection (analyzes Unicode ranges for Hangul, CJK, Kana, Latin)
- Default enhance to off (hurts clean digital images)
- Replace Auto/AI/Fast buttons with Fast/Balanced/Best (consistent with other tools)
- Rename "Denoise" to "Noise Reduction" with explanatory subtitle
- Change output format from 3 buttons to dropdown with all formats (PNG, JPG, WebP, AVIF, TIFF, GIF, HEIC, HEIF)
- Add HEIC/HEIF input decoding (was missing unlike other tools)
- Add HEIC/HEIF/AVIF output conversion via Sharp and heif-enc
- Generate browser-compatible WebP preview for non-previewable output formats
- Fix torchvision compatibility shim so Real-ESRGAN actually loads (was silently falling back to Lanczos)
- Fix denoise crash: Image.fromarray() instead of type(img).fromarray()
- Redirect stdout for entire AI pipeline to prevent library messages corrupting JSON output
- Add GFPGAN model download for face enhancement
- Use batch endpoint for multi-file uploads (enables Download All ZIP)
- Fix multi-image: process selected file, not always first
- Fix progress bar: asymptotic fill prevents visual stalling
- Fix slider: write results to captured index, not current selection
- Add model selection (Auto/AI/Fast), face enhancement, denoise
- Add output format (PNG/JPEG/WebP) with quality control
- Add Upscale All for sequential batch processing with queue
- More granular Python progress stages for smoother UX
- Fix temp file leak: clean up preprocessed image in finally block
- Log warning instead of silently swallowing preprocessing failures
- Simplify auto_detect_language to honest default (was a stub that
wasted time loading a model but always returned "en")
Ultra quality (People only):
- BiRefNet-matting ONNX (928MB) for true alpha matting with per-pixel
transparency on hair wisps and fine edges
- Custom rembg session class, zero new Python dependencies
- Model pre-downloaded in Docker build for immediate availability
Quality tier labels: Fast / HD / Max / Ultra (shorter, fits 4-col grid)
Adds a new "Ultra" quality tier for People subject type that uses
BiRefNet-matting (ONNX, 928MB) for true alpha matting instead of
binary segmentation. Produces per-pixel transparency for hair wisps
and fine edges that standard models miss.
- Custom rembg session class loads BiRefNet-matting ONNX from GitHub releases
- Zero new Python dependencies (reuses existing onnxruntime)
- Model pre-downloaded in Docker build alongside existing models
- Ultra option only visible when subject is People
- Falls back to Best when switching to Products/General
Remove Background:
- Two-phase flow: AI removes bg once, then effects adjust instantly
- Blur background effect with real-time CSS preview (portrait mode)
- Drop shadow effect with opacity control
- Gradient backgrounds with presets, custom colors, and angle
- Custom background image upload (including HEIC/HEIF)
- Solid color backgrounds moved from Python to Node.js/Sharp
- Effects-only API endpoint for instant re-renders without AI re-run
- HEIC/HEIF input support (decoded before passing to Python/rembg)
- Passport/ID photo checkbox defaults ON for People subject
- Before/after slider preserved when no effects active
- 15 comprehensive Playwright e2e tests
Color Tools:
- Consolidated 4 tools (brightness-contrast, saturation, color-channels,
color-effects) into single "Adjust Colors" tool
- Added exposure, temperature, tint, hue, sharpness controls
- SVG filter-based live preview for all adjustments
- Backward-compatible URL redirects from old tool paths
Other fixes:
- Favicon tool: download button instead of auto-download
- Batch processing: HEIC filename extension fix
- File store: processedFilename field for proper batch downloads
- Add bidirectional HEIF support: decode (input) and encode (output) via system heif-convert/heif-enc
- Add server-side WebP preview generation for non-browser-previewable formats (HEIC, TIFF)
- Fix content-aware resize failing on HEIF input (decode before passing to caire)
- Fix content-aware resize timeout on large images by downscaling to max 1200px and using JPEG intermediate
- Add HEIF as target format in convert tool
- Add loading spinner for HEIF preview decode in file store
- Fix file picker not accepting HEIF files (explicit .heic,.heif,.hif extensions)
- Extend frontend timeout for medium tools to 180s with 45s progress animation
- Redesign rotate controls with preset buttons and compact flip section
- Remove misleading savings percentage from convert tool
Replace the Python seam-carving library with caire (esimov/caire v1.5.0),
a Go-based content-aware resize engine that is faster and supports both
shrinking and enlarging via seam insertion.
- Add Go builder stage in Dockerfile to compile caire from source
- Rewrite seam-carving.ts to call caire via execFile (no Python sidecar)
- Remove content-aware-resize from PYTHON_SIDECAR_TOOLS (60s timeout)
- Add new options: blur radius, edge sensitivity, square mode, face detection
- Move content-aware toggle below standard resize in UI (subtler placement)
- Rename "Don't enlarge" to "Limit to original size" with hover tooltip
- Add smooth progress bar for medium-duration tools
- Delete seam_carve.py and remove seam-carving pip dependency
- Update integration tests and visual regression screenshots
- Replace Rule of Thirds button with checkbox for clearer toggle
- Show side-by-side comparison after crop instead of overlay slider
- Add custom aspect ratio option with W:H number inputs
- Fix batch crop failing on files with different dimensions by
sending percentage-based coordinates instead of absolute pixels
- Add failed-file error state display in tool page
Several RBAC features from feat/rbac-permissions were silently lost
during the merge into main. This restores and completes them:
- Add permissions and teamName to login/session API responses
- Export Permission and Role types from shared package
- Filter settings tabs by user permissions in frontend
- Extend useAuth hook with role, permissions, and hasPermission
- Restrict teams listing to admin only
- Add admin override for API keys, files, and pipelines listing
- Add ownership scoping to file access, download, and delete routes
- Register userFileRoutes in integration test server
- Mock auth import in unit permissions test to avoid SQLite lock
Replace requireAuth with requirePermission("pipelines:own") on pipeline
save/list/delete routes. Admin users with pipelines:all can see and
delete all pipelines. Unauthorized delete returns 404 to avoid leaking
resource existence.
Migrate settings, teams, branding, and user management routes to use
the new permission-based authorization system. Remove requireAdmin
function entirely.
Create the RBAC permission module that maps roles to permissions and
provides a requirePermission middleware to replace requireAdmin. Update
the test server to use requirePermission for the admin health check.
- Remove all lite/full variant logic from frontend, API, shared constants,
docs, and tests (single unified Docker image only)
- Replace single QEMU multi-arch Docker build with per-architecture native
builds (amd64 + arm64) and manifest merge to fix disk space exhaustion
- Add disk cleanup step and per-platform build cache scopes
- Switch release trigger from push to workflow_dispatch
- Add GitHub issue templates and PR template
GPU-dependent libraries (paddlepaddle-gpu, torch CUDA, realesrgan)
cannot be imported at Docker build time because the CUDA driver is
only available at runtime. Smoke test now verifies CPU-only imports
(rembg, cv2, numpy, mediapipe, seam_carving) and checks that model
files exist on disk. GPU imports are verified at runtime.
paddlepaddle-gpu requires libcuda.so.1 at import time, but no CUDA
driver exists during Docker build. The download script now catches
this ImportError and skips PaddleOCR model pre-download on amd64.
Models will download on first use at runtime when the CUDA driver
is available via nvidia-container-toolkit.
On arm64 (CPU paddlepaddle), models are still pre-downloaded at build
time as before.
Also reverted CI to amd64-only Docker build test for speed. Multi-arch
build is tested on release via the release workflow.
paddlepaddle-gpu needs libcuda.so.1 at import time, but the real NVIDIA
driver is only injected at runtime by the container toolkit. Install
cuda-compat-12-6 which provides forward-compat stubs that satisfy the
dlopen without a real GPU. Also force CPU mode via env vars in the
download script.
paddlepaddle-gpu tries to load libcuda.so.1 on import, but no GPU
driver exists during Docker build. Set PADDLE_DEVICE=cpu, FLAGS_use_cuda=0,
and CUDA_VISIBLE_DEVICES="" before any ML imports to force CPU mode.
-i replaces the entire package index so paddleocr couldn't be found.
Use --extra-index-url to add PaddlePaddle's index alongside PyPI, and
install paddleocr separately so it resolves from PyPI.
paddlepaddle-gpu==3.0.0 is not on PyPI, it is hosted on PaddlePaddle's
own package index. Added -i flag pointing to the cu126 stable index
for the amd64 GPU build.
- Warn on startup if deprecated STIRLING_VARIANT env var is set
- Broaden upscale.py exception handling to catch RuntimeError/OSError
for Lanczos fallback (not just ImportError)
- Add QEMU + multi-arch (amd64+arm64) to CI Docker build test
- Use .get() instead of .all() for single-row health check query
- Restore container_name in docker-compose.yml for backwards compat
PaddleOCR prints download/init messages to stdout which corrupts the
JSON result that the bridge expects. Same risk with basicsr/realesrgan.
Applied the same fd-level stdout redirect pattern already used in
remove_bg.py: redirect fd 1 to stderr during ML work, restore for
the JSON result. Also added show_log=False to PaddleOCR constructor.
node --import tsx requires tsx to be directly in node_modules/, but
pnpm hoists it differently. npx tsx works because it resolves through
pnpm's bin links. tini as PID 1 handles signal forwarding regardless.
basicsr has a known torchvision.transforms.functional_tensor compat
issue on arm64 with newer torchvision. On arm64, upscale.py falls back
to Lanczos via ImportError anyway. Smoke test still verifies the model
weights file exists on all platforms.
PaddleOCR uses its own language codes (ch, japan, korean, latin) not
ISO codes (zh, ja, ko, de, fr, es). The download script and ocr.py
now map API language codes to PaddleOCR codes correctly. German,
French, and Spanish all use the "latin" script model.
Rewrite docker-tags.md for single image with GPU auto-detection.
Update deployment.md to remove variant table and lite/cuda references.
Replace LaMa Cleaner references with OpenCV in architecture and AI docs.
Add migration notes for users on :lite and :cuda tags.
Remove 3-variant matrix (full/lite/cuda). Single build produces
a multi-arch manifest (amd64 + arm64) pushed to Docker Hub and GHCR.
Tags: latest, X.Y.Z, X.Y, X. CI builds native platform only (amd64)
for speed. Multi-arch only on release.
- Add 8s shutdown timeout to prevent indefinite hang when app.close()
stalls. Stays under Docker's default 10s stop_grace_period.
- Health endpoint now checks database connectivity, returns 503 when
DB is unreachable so Docker marks container unhealthy.
- Removed variant field from health response (single image now).
GPU is activated at runtime via --gpus all, not a separate compose file.
Added log rotation (10MB x 3 files) to prevent disk fill on long-running
instances. Removed docker-compose.gpu.yml.
- Remove VARIANT/GPU build args, single image for all platforms
- amd64: nvidia/cuda base with GPU Python packages
- arm64: node base with CPU Python packages
- Add tini as PID 1 for proper signal handling
- Replace npx tsx with node --import tsx
- Split pip install into base + tool layers for better caching
- Add NVIDIA_VISIBLE_DEVICES env vars for container toolkit
- Suppress Python ML library log noise
- Increase healthcheck start-period to 60s
- Remove STIRLING_VARIANT env var
- Remove lama-cleaner from pip installs
Downloads all rembg models (6), RealESRGAN_x4plus.pth weights,
PaddleOCR models for all 7 supported languages, verifies MediaPipe
bundles its face detection models. Runs a final smoke test importing
every ML library. Any failure exits non-zero, failing the Docker build.
lama-cleaner is pip-installed but never imported in any Python script.
inpaint.py uses OpenCV TELEA. Removing saves ~100+ MB of image size.
Also added seam-carving to requirements-gpu.txt where it was missing.
model_path was None, so the model had random weights and always fell back
to Lanczos. Now loads RealESRGAN_x4plus.pth from /opt/models/realesrgan/
(configurable via REALESRGAN_MODEL_PATH env var). Only falls back to
Lanczos on ImportError, not blanket Exception.
User-uploaded files were stored in /app/data/files (container writable layer)
instead of /data/files (persistent volume) because the env var was not set in
the Dockerfile. Files were lost on container recreation.
Replace static llms.txt and llms-full.txt with auto-generated versions
that stay in sync with docs on every build. The plugin also generates
per-page .md files for individual page fetching by LLMs.
New tool for joining images horizontally or vertically,
distinct from the grid-based collage tool. Preserves aspect
ratios with fit/original resize modes, optional gap, and
multi-format output.
Users running lite mode had no way to tell why AI tools were greyed out.
Now the public health endpoint reports the variant, and a visible banner
appears in the tool panel when running in lite mode.
Prevent silent data corruption when the API is called directly
(bypassing UI guards). Binary/complex EXIF fields like MakerNote
are now filtered from fieldsToRemove in the image-engine operation.