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
Merge CPU, CUDA, and lite Docker images into a single unified image.
One tag (latest) works on all platforms: amd64 (NVIDIA CUDA) and arm64 (CPU).
GPU auto-detected at runtime. All ML models and packages baked in.
Key changes:
- Platform-conditional Dockerfile (nvidia/cuda on amd64, node on arm64)
- tini as PID 1 for proper signal handling
- Fix FILES_STORAGE_PATH data loss bug
- Fix RealESRGAN upscaler (was broken, always fell back to Lanczos)
- Fix PaddleOCR language codes and stdout corruption
- Simplified CI/CD (single build, single tag)
- Expanded model pre-download with verification
- Shutdown timeout, improved health endpoint
- Remove unused lama-cleaner
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.