- 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
- Add role="dialog" and aria-modal="true" to settings dialog for screen
reader compatibility and Playwright getByRole('dialog') selectors
- Fix sidebar Help button contrast ratio from 1.18:1 to WCAG AA compliant
by using text-sidebar-foreground class
- Add explicit tabIndex={0} to search input for keyboard navigation
- Update convert test to expect BMP success (now a supported format)
- Fix watermark-image tiled test MIME type mismatch (webp not png)
- Swap compose test base/overlay so overlay is smaller than base
- Relax find-duplicates perceptual hash grouping assertions
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
- Replace naive 4-neighbor flood fill with scanline algorithm for performance
- Include alpha channel in color distance calculation
- Render actual mask outline with marching ants instead of bounding rectangle
- Use Konva Shape with batched canvas path for efficient edge rendering
- Add dedicated magicWandContiguous state (was incorrectly sharing fillContiguous)
- Add Contiguous checkbox to magic wand options bar
- Fix invertSelection to use consistent mask values (1 not 255) and expand
inverted mask to full canvas dimensions instead of staying within old bounds
- 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.
Add import button to saved pipelines section with file picker,
validation, and error display with auto-dismiss. Section now
always renders with empty state text when no pipelines exist.
- Replace bg-popover with bg-card for dropdown backgrounds (popover CSS
variable is not defined in this project's theme)
- Move menu bar test to tests/e2e-editor/ and adapt for editorPage fixture
- Fix click-outside test to handle welcome screen overlay
Recovers the editor menu bar (File/Edit/Image/Layer/Select/Filter/View)
from orphaned commit b07ecd5 that was lost during a rebase. Integrates
it into the current editor-page layout with PSD/TGA/EXR/HDR file open
support and new document dialog.
- fix WebP export silently producing PNG when background is non-transparent
- fix autosave not converting blob: URLs inside image-type canvas objects
- fix project load not resetting selection/crop/clipboard state
- fix rotateCanvas not updating object rotation attributes
- fix flipCanvas not negating object rotation attributes
- fix line shadow props overridden by effect spread ordering
- fix "outside" stroke position rendering same as "center"
- add missing pencil tool keyboard shortcut (N)
- remove misleading resample dropdown from image resize dialog
- fix E2E autosave tests for production builds (no Vite dynamic imports)
- fix color picker test case sensitivity (CSS uppercase vs DOM text)
- add 4 unit tests for rotation attribute transforms
- Reduce per-tool time from 15 to 10 frames (total montage: 480 frames)
- Update total composition duration from 840 to 600 frames in Root.tsx
- Increase micro-visualization from 2 to 4 frames for visibility
- Add varied entry paths: diagonal entries from all 8 directions instead
of just left/right/top
- Adjust all act timings (grid pulse, category labels, tagline) to match
the compressed timeline
- ToolPill: add box-shadow (0 2px 8px), gradient overlay for glossy depth,
subtle white border for definition
- ToolGalaxy: fix tagline so counter number appears before "tools. Zero cloud
dependency." text instead of being separated
Use real screenshots and sample photos from apps/videos/public/screenshots/
instead of CSS-gradient PhotoPlaceholder components:
- PipelineFlow: landscape photo for thumbnails with object-position variation
- AiMagicReel: portrait photo for bg removal, B&W photo for colorization,
landscape for super resolution and restoration segments
- PrivacyPromise: real portrait/landscape/B&W photos in shield, larger padlock
- OneCommand: real app-full.png screenshot instead of wireframe AppMockup,
increased amber glow to 12%
- CloudVsLocal: real portrait photos for all thumbnails, larger server icons
(36px), amplified shake (5px) and vibrate (3px), escalated red tint overlay
- ShieldIcon: increased stroke width to 3.5px, added gradient fill
- 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
Tracks from Mixkit (royalty-free, no attribution required):
- PromoTeaser: "Life's a Movie" by Arulo (50% vol)
- XLaunchVideo: "Techno Fest Vibes" by A.M. (40% vol)
- ProductDemo: "Rising Forest" by Diego Nava (30% vol)
The containerType: inline-size CSS containment prevented the image from
expanding its parent container, causing the preview to render at 0x0px.
Removed inline-block and containerType, switched font sizing to vw units.
- 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
5-act composition (540 frames, 1920x1080): split screen with cloud vs
local comparison, escalating chaos on the cloud side while local stays
calm, resolving to full-screen "Your images never leave your network."
Also updates Root.tsx to register all 10 landing page compositions,
removes ComponentTest, and resolves merge conflict markers.
Restore missing landing page compositions (FloatingTools, BrandGradient,
FormatUniverse) lost during branch merge, and fix the @/ path alias in
remotion webpack config to support all composition imports.
The meme generator had everything in one component crammed into the
narrow right sidebar. This splits it into the proper two-panel
architecture (like collage/qr-generate):
- meme-store.ts: shared Zustand store for all meme state and actions
- meme-generator-preview.tsx: ResultsPanel (main area) with gallery,
layout picker, editor preview with CSS text overlay, and result view
- meme-generator-settings.tsx: Settings sidebar with text inputs, font
picker, colors, alignment, and generate button
- tool-registry.tsx: adds ResultsPanel to the meme-generator entry
Also fixes text overlay sizing (reduced from 0.6cqi to 0.45cqi with
smaller stroke width) and moves font injection to a standalone function
callable from either component.
Eight scenes replicating the SnapOtter UI in light theme: Dashboard
overview with search filtering, single tool resize flow, batch
processing with staggered progress, pipeline builder, AI tools
(bg removal, upscale, OCR), image editor, API docs, and end card.