The store set an initial 5% optimistically, then SSE sent real progress
starting from 0%, causing the bar to visibly drop. Now progress only
moves forward — both SSE and polling paths use Math.max to never
regress below the current value.
The bottom-right install indicator showed "1/6 installed" even when
installing a single bundle, implying all 6 would be installed. Now the
counter only appears during "Install All" operations.
Replace FeatureInstallPrompt's local SSE/polling with useFeaturesStore
so install progress, errors, and recovery are handled globally — works
across navigation, logout/login, and partial downloads. Shows fun
progress messages and ETA matching the settings page.
Add compose healthcheck and optional GPU profile (--profile gpu).
Replace static "Preview not available" and "Cannot preview this image
format" messages with a loading spinner in ImageViewer and SplitCanvas,
since HEIC files get server-side conversion and the preview appears
after a few seconds. Also default showDetails to true on /fullscreen.
The home page's file-uploaded view rendered its own tool list without
checking AI feature install status, so download icons disappeared after
uploading a file. Now both Quick Actions and All Tools sections show the
download badge consistently with the sidebar ToolPanel.
Uses XMLHttpRequest upload progress to track image upload (0-80%),
then shows "Processing collage..." for the server-side compositing
phase (80-100%). Progress bar with percentage shown in the preview area.
Adds objectFit property to CellTransform (cover/contain). When set to
"contain", the entire image is shown within the cell with background
color fill. Toggle button in the cell controls toolbar switches between
modes. Server-side rendering handles both modes via Sharp.
- Fix pan: use `movement` (cumulative) instead of `delta` (per-frame)
in useDrag — delta accumulation against static memo was wrong
- Fix wheel zoom: replace useWheel with direct DOM addEventListener
using { passive: false } so preventDefault() works and page doesn't
scroll while zooming
- Fix controls: remove auto-hide timer, keep zoom slider and reset
button visible while cell is selected
The lock file stored process.pid (Node server PID) which is always
alive. When releaseInstallLock wasn't called (e.g., error path),
the lock persisted forever, making the bundle stuck in "installing"
and blocking all future installs.
Fix: remove PID from lock entirely. Use simple file existence as
mutex. On server startup, unconditionally delete any lock file.
The previous selector returned the isToolInstalled function reference
which never changes, so the component didn't re-render when bundles
loaded. Now subscribes to the bundles array directly via useMemo.
- Subscribe to features store reactively in ToolPage so refresh shows
install prompt correctly instead of the tool UI
- Show loading state while features are being fetched for AI tools
- Capture stdout from install script for better error messages
- Keep last 20 stderr lines for error context instead of just exit code
- Clear install progress on success (was leaving stale state)
- Pass PIP_CACHE_DIR to install subprocess
The guard was only present on restore-photo.ts and the createToolRoute
factory. All other AI tools used custom route handlers that bypassed
the check entirely, allowing requests to reach the Python sidecar even
when the feature bundle was not installed. This adds an early 501
response before any multipart parsing or file processing.
The on-demand feature download system stores models at /data/ai/models/
(set via MODELS_PATH env var), but all Python scripts hardcoded
/opt/models/ as the base path. Each script now reads MODELS_PATH and
falls back to /opt/models for backward compatibility.
Remove all ML pip installs (onnxruntime, rembg, realesrgan, paddlepaddle,
mediapipe, codeformer), model downloads, and post-install fixups from the
Dockerfile. The base image now ships only Node.js + Sharp + Python with
numpy/Pillow/opencv. AI features are installed on-demand at runtime via
the feature manifest and install_feature.py script.
Key changes:
- Remove SKIP_MODEL_DOWNLOADS build arg (no longer needed)
- Remove apt-get purge of build-essential (needed for runtime pip installs)
- Remove PaddleX symlinks and facexlib weight directory setup
- Add COPY of feature-manifest.json and install_feature.py
- Update PYTHON_VENV_PATH to /data/ai/venv, add MODELS_PATH and DATA_DIR
- Entrypoint bootstraps AI venv from /opt/venv on first container start
with crash-safe temp directory pattern
Show an install prompt instead of the normal tool UI when an AI feature
bundle is not installed. Admins see a one-click install button with SSE
progress tracking and polling fallback; non-admins see a message to
contact their administrator.
Show a download icon on AI tools that are not yet installed, and fetch
feature status on mount in both the sidebar tool panel and fullscreen
grid page.
Add Zustand features store for tracking AI feature bundle state with
fetch, refresh, isToolInstalled, and getBundleForTool methods. Extend
parseApiError to return structured FeatureNotInstalledError objects
when the backend returns FEATURE_NOT_INSTALLED, and handle them in
both tool and pipeline processor hooks with user-friendly messages.
Check installed.json before exec()-ing AI scripts so that requests
for uninstalled feature bundles return a structured error instead
of crashing with an ImportError. Also sets U2NET_HOME to the
bundled model directory when present.
Return 501 with structured error when an AI tool's feature bundle
is not installed, preventing Python ImportError crashes. Guards
added to tool-factory, batch, pipeline (both validation loops),
and restore-photo custom route.
Reads the feature manifest, installs pip packages (common + arch-specific),
downloads models in parallel with atomic rename, and writes installed.json.
Includes disk space pre-check, NCCL conflict handling, retry logic, and
progress reporting via stderr JSON lines. Also updates the feature route
to pass manifestPath and modelsDir as CLI arguments.
Tracks which AI feature bundles are installed via /data/ai/installed.json
with atomic writes, in-memory caching, file-based install locks, and
startup recovery for interrupted installs.
Authoritative JSON manifest defining all 6 AI feature bundles with:
- Exact pip package versions and platform-specific variants (amd64/arm64)
- pip flags (--no-deps for codeformer, --extra-index-url for torch/paddle)
- postInstall re-pins (numpy==1.26.4 after codeformer)
- Model download entries (direct URL, rembg sessions, HuggingFace snapshots)
- Bundle-to-tool mapping matching shared/features.ts
Used by install_feature.py at runtime to install bundles on demand.
- Added model mismatch warnings in colorize, enhance-faces, and upscale routes.
- Improved error handling in colorize, enhance_faces, remove_bg, restore, and upscale scripts with detailed logging.
- Updated Dockerfile to align NCCL versions for compatibility.
- Introduced a new full tool audit script to test all tools for functionality and GPU usage.
- Created Playwright E2E tests for GPU-dependent tools to ensure proper functionality and performance.