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
- 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
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.
Batch progress was broken because JobProgress events lacked a `type`
field. The frontend checks `data.type === "batch"` to distinguish batch
from single-file SSE events, so batch progress was silently discarded
and multi-file processing appeared stuck at 15%.
Also improves the processing UX for non-AI (Sharp-based) tools: the
progress bar now pulses during the server processing phase and shows
a "This may take a moment" hint after 10 seconds.
Co-authored-by: Siddharth Kumar Sah <siddharth123sk@gmail.com>
* feat: add resolveOutputFormat utility for input format preservation
* fix: preserve file order in batch processing with X-File-Results header
Collect all results before streaming the ZIP to guarantee upload order.
Replace X-File-Order with index-based X-File-Results header that maps
each upload index to its processed filename, handling failures and
duplicate filenames correctly.
Closes#13
* fix: use X-File-Results for index-based batch file matching
The frontend now matches processed files to entries by upload index
instead of fragile name/position matching.
* feat: preserve input format in smart-crop with quality control
Smart crop now outputs in the same format as the input (JPG in, JPG out)
instead of always converting to PNG. Adds an optional quality setting
(default 95) for lossy formats.
Closes#14
* feat: add output quality slider to smart crop settings UI
* feat: preserve input format in crop tool
* feat: preserve input format in color adjustment tools
Applies to brightness-contrast, saturation, color-channels, and
color-effects tool routes.
* refactor: avoid double encode in smart-crop content mode
For the simple trim path (no pad-to-square), chain .toFormat() on the
trim pipeline directly instead of creating a second Sharp instance.
This eliminates a redundant intermediate encode that degraded quality
for lossy formats. Also use trimmed.info dimensions instead of a
separate metadata() call for the pad-to-square path.
---------
Co-authored-by: Siddharth Kumar Sah <siddharth123sk@gmail.com>
Centralize duplicated getToken() + Bearer header logic into a single
formatHeaders() helper in lib/api.ts. When no token exists, the
Authorization header is omitted entirely instead of sending an empty
Bearer token, which breaks forward-auth proxies like Authelia behind
Caddy.
Changes:
- Add formatHeaders() with try-catch around localStorage access
- Replace 20+ duplicated getToken() definitions across tool components
- Migrate all call sites including file-details, settings, change-password
- Update tests to verify header omission on empty token
Based on the fix proposed by @jules2689 in #6, with improvements:
file placement (lib/api.ts vs components), localStorage error handling,
simplified truthiness check, and complete call-site coverage.
Co-Authored-By: Julian Nadeau <julian@jnadeau.ca>
Adds a new mode that trims uniform-color borders around the subject,
like GIMP's "Crop to Content." Includes configurable tolerance threshold
and optional pad-to-square with target size for e-commerce workflows.
The original attention-based crop is preserved as "Focus Crop" mode.
Closes#7
New users on dark-mode systems were seeing dark theme on first visit.
The default is now explicitly light, matching the API's DEFAULT_THEME.
Users can still switch to dark or system in settings.
Update all references across docs, workflows, UI components, and config
to point to the new GitHub org (stirling-image/stirling-image) and Docker
Hub account (stirlingimage/stirling-image) ahead of repo transfer.
Add a privacy policy page accessible at /privacy (public, no auth required).
Relax Content-Security-Policy for /api/docs route to allow Scalar's inline
script initialization, fixing blank docs page in production.
The processed image in the BeforeAfterSlider was layered on top of the
original with a semi-transparent background. When the processed result
had transparency (e.g. after remove-background), the original image
showed through, making it look like the background was not removed.
Added an opaque checkerboard background behind the processed image so
transparent areas are clearly visible instead of showing the original.
Two issues caused intermittent step addition failures in the automation
pipeline:
1. RemoveBgControls had onChange in its useEffect deps. Since onChange is
a new function reference on every parent render, this created an
infinite re-render loop (effect -> setState -> render -> effect).
Fixed by using the onChangeRef pattern matching other settings
components.
2. All step mutation callbacks read from stepsRef.current and passed
values to setSteps. Concurrent callbacks (e.g. addStep + a settings
effect) would overwrite each other. Fixed by switching to functional
state updates (setSteps(prev => ...)) and removing stepsRef.
Also rewrites automate e2e tests to use manual step addition instead of
referencing templates that no longer exist in the UI.
Child component effects (Controls onChange) fire before the parent's
useEffect that synced stepsRef. This caused updateStepSettings to read
stepsRef.current as [] and wipe out newly added steps.
Fix: assign stepsRef.current = steps directly during render so the
ref is always current before any child effects execute.
All step mutation callbacks (addStep, removeStep, moveStep,
updateStepSettings) captured `steps` in their useCallback closures.
When React batched state updates, rapid interactions could use a stale
steps array, causing clicks to silently fail.
Fix: use a stepsRef that always holds the latest value. Callbacks read
from stepsRef.current instead of the captured closure variable, and no
longer need `steps` in their dependency arrays.
When a tool was selected via search, the search text persisted after
the picker closed. Reopening the picker showed a filtered list instead
of all tools, making it look like clicking tools without searching
didn't work.
The settings panel used conditional rendering ({isExpanded && ...})
which unmounted the Controls component on collapse, losing all state.
Switch to CSS hidden class so the component stays mounted and settings
persist when the panel is collapsed and re-expanded.
Extract a *Controls subcomponent from all 16 pipeline-compatible tool
settings components. Each Controls component holds the UI state and
settings controls, accepts an onChange callback, and uses useRef to
prevent infinite re-render loops. The standalone *Settings components
become thin wrappers that add useToolProcessor, useFileStore, and
action buttons.
pipeline-step-settings.tsx is rewritten from ~675 lines to ~55 lines:
the entire TOOL_FIELDS declarative map and generic renderer are deleted
and replaced with direct imports of the Controls components. Pipeline
steps now render the exact same UI as standalone tool pages.
Special cases:
- CropControls: numeric inputs for pipeline (standalone uses canvas)
- RotateControls: resetSignal prop for post-processing reset
- ColorControls: accepts toolId for tab selection
- StripMetadataControls: checkboxes only (no file inspection)
- RemoveBgControls: already extracted, unchanged
Extract the settings controls (subject type, quality, background color)
from RemoveBgSettings into a shared RemoveBgControls component that
accepts settings + onChange props. Both the standalone tool page and the
pipeline step configurator now render the same component, so the UI is
identical and changes only need to be made in one place.
The pipeline step settings had an empty array for remove-background,
showing "No configurable settings" even though the tool supports
model selection and background color. Add AI model selector (u2net,
birefnet-general-lite, birefnet-general, birefnet-portrait, bria-rmbg)
and background color picker matching the standalone tool's options.
Register remove-background, upscale, and blur-faces in the pipeline
tool registry via registerToolProcessFn(). These tools keep their
custom HTTP routes (with progress callbacks) for direct use, but now
also provide a simple process function for pipeline/batch execution.
Add a search bar to the pipeline tool picker so users can quickly
find tools by name or description. Uses the existing SearchBar
component and the same filtering pattern as the main tool panel.
Update tests to reflect that these 3 AI tools are now pipeline-
compatible (moved from excluded to included assertions).