Custom route handler supporting template mode (JSON body with templateId)
and custom image mode (multipart upload). Registers process function for
pipeline compatibility. Includes 18 integration tests.
- Add ppm/pgm/pbm to CLI_DECODED_FORMATS with Sharp-first, ImageMagick fallback
- Create sample.qoi test fixture (10x10 solid color)
- Add QOI to format-matrix test FORMAT_SAMPLES
- Mark PPM/PGM/PBM as needsCliDecoder in tests (Sharp doesn't support them natively)
Add colorBlindness() operation with 8 simulation matrices (Vienot/Machado)
for protanopia, deuteranopia, tritanopia, protanomaly, deuteranomaly,
tritanomaly, achromatopsia, and blue cone monochromacy.
fix: add mediapipe to upscale-enhance bundle for face enhancement
The enhance-faces tool requires MediaPipe for face detection, but the
upscale-enhance feature bundle did not include mediapipe in its pip
packages. Added mediapipe to both amd64 and arm64 package lists.
Added 25 regression tests validating bundle dependency completeness.
Closes#129
The enhance-faces tool requires MediaPipe for face detection, but the
upscale-enhance feature bundle did not include mediapipe in its pip
packages. Users who installed only the upscale-enhance bundle got
"Face detection requires MediaPipe" errors. Added mediapipe to both
amd64 and arm64 package lists, matching the pattern used by the
face-detection and photo-restoration bundles.
Also added feature-manifest.test.ts with 25 tests validating bundle
dependency completeness to prevent similar missing-dependency bugs.
Closes#129
The default-view redirect in HomePage fired on every mount, not just the
initial page load. A module-level flag now gates the redirect so it only
applies once per session, allowing users to switch to sidebar view when
grid is the default.
Closes#128
Users can no longer customize the app name or logo. The branding API
endpoints, permission, frontend UI, env vars (APP_NAME, MAX_LOGO_SIZE_KB),
and all related tests are removed. Includes a migration to clean up
branding data from existing databases.
The content-aware-resize tool had a full backend implementation (API route,
caire binary, seam-carving bridge) but was missing from the frontend
toolRegistry Map. Navigating to /content-aware-resize showed "Tool not found"
because ToolPage could not resolve a registry entry for the tool ID.
Added a dedicated ContentAwareResizeSettings component and registered it in
the tool registry with side-by-side display mode. Also added a guard test
that verifies every tool in the shared TOOLS[] array has a matching registry
entry, preventing this class of bug from recurring.
Closes#131
- Fix resize 20% failure rate: add Zod refine requiring at least one
dimension, enforce integer/max constraints, clamp percentage scaling
to minimum 1px, and guard against missing metadata in withoutEnlargement
- Fix PostHog init race condition: move consent check before async import
so frontend events (search, pageview) are no longer silently dropped
- Fix identify() passing nested $set/$set_once wrappers instead of flat
properties, so version person property now appears on PostHog profiles
- Add error_code and error_message to failed tool_used analytics events
for debugging tool failures from PostHog
The QR code generator's logo feature was broken in production (Docker)
due to three interacting issues:
1. The CSP connect-src directive did not include data:, so the
qr-code-styling library's internal XHR to convert logo data URLs to
blobs was silently blocked. The library has no onerror handler, so the
render promise hung forever after the container was already cleared.
2. crossOrigin: "anonymous" was unnecessarily set on imageOptions for
data URLs, which can cause canvas taint issues.
3. The logo options used a conditional spread that omitted the image key
when no logo was set. The library's update() deep-merges options, so
removing the logo preserved the stale data URL and the QR stayed
broken even after logo removal.
Closes#121
The desktop sidebar never rendered the custom logo because only mobile
views used the customLogo state. Added a logo section at the top of
the desktop sidebar that displays the custom logo (or the default
OtterLogo when none is set).
The upload handler used raw fetch() without checking response.ok, so
HTTP 4xx errors (e.g. file too large) were silently ignored and the UI
falsely reported success. Now checks response status and surfaces the
server error message.
Closes#125
The upscale function called runPythonWithProgress without a timeout parameter,
defaulting to the bridge's 10-minute hard limit. On CPU-only systems like
Synology NAS devices, Real-ESRGAN 4x upscaling easily exceeds this for modest
images. Additionally, when the timeout fired on the dispatcher path, the Python
process was left running and blocked all subsequent AI operations.
This fix adds an adaptive timeout based on input megapixels, scale factor, and
GPU availability (180s/effective-MP on CPU, 30s/effective-MP on GPU, floor of
10 minutes). It also kills the dispatcher on timeout so subsequent requests can
proceed via a fresh restart.
Closes#119