- Add meme-generator tool and meme-templates API to OpenAPI spec, VitePress
docs, and README
- Remove 4 phantom OpenAPI entries (brightness-contrast, saturation,
color-channels, color-effects) that were consolidated into adjust-colors
- Bump OpenAPI version to 1.16.0, tool count to 51
- llms.txt and llms-full.txt auto-update from OpenAPI at runtime
CLAHE width/height is tile size in pixels, not tile count. A 3px tile on
a 992x1088 image created ~330x360 independent histogram regions, producing
crosshatch/etching artifacts. Now uses image_dimension/8 (clamped 8-256)
for ~8 tiles per axis. Also strips alpha before enhancement and re-joins
after to prevent CLAHE/normalise/linear from corrupting transparency.
- Fix SSE write-after-end crash in progress.ts (remove callback before ending stream)
- Fix blob URL memory leaks: revoke processedPreviewUrl and old HEIC preview URLs
- Add AbortController to batch fetch in use-tool-processor and use-pipeline-processor
- Fix TGA format misidentified as CUR (extension overrides magic bytes)
- Add libheif-plugin-libde265 to Docker for HEIC/HEIF decode support
- Remove unused imports and state (AppLayout, setSampledColor, useEffect)
- Fix non-null assertions in meme-text-renderer and meme-generator
- Fix confusing void type in meme-templates
- Remove unnecessary useEffect deps in adjustments-panel
- Fix Playwright strict mode violations in 5 E2E tests
When deepEnhance is true and the upscale-enhance bundle is installed,
runs SCUNet (quality tier) after the Sharp pipeline for noise/artifact
cleanup. Falls back gracefully to Sharp-only if sidecar is unavailable.
CLAHE provides adaptive local contrast, normalise stretches the
histogram, and gamma adjusts exposure perceptually. Replaces the old
modulate/linear pipeline that compounded errors and darkened images.
Preset multipliers now include clahe and normalise entries.
Key fixes beyond the spec:
- maxSlope rounded to integer (Sharp requirement)
- White balance uses linear() instead of recomb() to avoid float-cast
that breaks CLAHE in the libvips pipeline
- CLAHE tile size adapts to image dimensions (1x1 for tiny images)
- Gamma clamped to Sharp's valid range (1.0-3.0)
- Normalise lower/upper correctly mapped to percentile cutoffs
- Remove editor from AppLayout so it owns the full viewport with no sidebar/padding
- Add back-arrow in menu bar for navigation, restyle menu bar to match native app feel
- Slim left toolbar from 48px to 36px with smaller icons and subtler dividers
- Fix options bar overlapping canvas via shrink-0 and overflow-hidden
- Fix opacity/blend controls clipping in right panel with min-w-0 on flex children
- Add global range slider CSS for consistent thin-track + round-thumb styling
- Make meme template routes public so img tags can load without Bearer auth
- Show pipeline export buttons in default compact view, not just expanded hover
- Queue individual AI feature installs instead of erroring when one is active
Recovers expanded export format support from orphaned commit 80961c01
and PSD export from b07ecd5. The editor export dialog now supports 7
formats (PNG, JPEG, WebP client-side; AVIF, TIFF, GIF, JXL via server
conversion). PSD export uses ImageMagick on the backend.
- Preview font size now calculated from actual container width, not viewport
- Preview stroke scales proportionally with font size (4% ratio)
- Server stroke reduced from 6% to 4% to match preview
- Added ResizeObserver to track container width for accurate sizing
- Added paint-order: stroke fill to CSS preview for consistent rendering
- Auto font size now capped at min(200, boxHeight/2, boxWidth/3) to prevent
oversized text on server render
- Text vertically centered within each text box
- Sidebar color labels shortened (Text/Stroke) with min-w-0 to prevent overflow
- Created meme-store.ts Zustand store for shared state between panels
- Split monolithic component into meme-generator-preview.tsx (main area)
and meme-generator-settings.tsx (sidebar)
- Added ResultsPanel to tool registry entry
- Fixed thumbnail URLs (.jpg -> .webp extension)
- Fixed text overlay sizing and positioning
Add beautify tool endpoint to OpenAPI spec with full parameter
documentation. Add tool to REST API docs Layout & Composition table.
Update tool count from 49 to 50 across README, docs homepage,
architecture docs, VitePress config, OpenAPI spec, and LLM docs
generator.
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)
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.
- 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