Commit Graph
201 Commits
Author SHA1 Message Date
SnapOtter 6e1b9865f1 feat(tools): 2.0 phase 5 wave 5a - image gap-fill (11 tools) (#225) 2026-06-13 10:19:16 +08:00
SnapOtter fc7c1f850e feat(tools): 2.0 phase 5 wave 4 - office, ebooks, data, archives (14 tools) (#224) 2026-06-13 10:19:11 +08:00
SnapOtter 638288e196 feat(tools): 2.0 phase 5 wave 3b - audio depth (14 tools) (#222) 2026-06-13 10:19:06 +08:00
SnapOtter 5f98b48593 feat(tools): 2.0 phase 5 wave 3a - video depth (22 tools) (#221) 2026-06-13 10:19:00 +08:00
SnapOtter 2f39e38162 feat(tools): 2.0 phase 5 wave 2 - pdf depth (21 tools) (#220) 2026-06-13 10:18:55 +08:00
SnapOtter ae1337901d feat(tools)!: SnapOtter 2.0 phase 4 wave 1: 45 core tools across all modalities (#219) 2026-06-13 10:18:49 +08:00
SnapOtter d647d8ed19 feat(modality)!: SnapOtter 2.0 phase 3 modality framework: media/doc engines, pool routing, display modes (#218) 2026-06-13 10:18:39 +08:00
SnapOtterandGitHub 6f276b4ef0 feat(a11y): WCAG 2.2 AA accessibility compliance (#209)
* feat(a11y): add i18n keys for ARIA labels and screen reader text

* fix(security): harden API against pentest findings

- Default TRUST_PROXY=false to prevent XFF rate limit bypass (PT-01)
- Return 400 instead of 500 on malformed JSON input (PT-03)
- Default MAX_PIPELINE_STEPS=20 to prevent DoS (PT-04)
- Validate clientJobId length (max 128) across all routes (PT-06)
- Add security headers to all reply.hijack() streaming responses (PT-07)
- Sanitize usernames in audit log to prevent stored XSS (PT-08)
- Block TRACE method with 405 response (PT-10)
- Add 429 RateLimited response to OpenAPI spec (PT-12)
- Default MAX_SVG_SIZE_MB=50 to limit SVGZ decompression (PT-13)
- Pin Dockerfile base images by digest
- Sanitize OIDC IdP error and sub claim in audit log
- Sync Docker compose/Dockerfile defaults with env.ts

* feat(a11y): convert all hardcoded aria-labels to i18n keys

Replace 49 hardcoded aria-label="..." strings across 25 files with
their corresponding t.a11y.* and t.common.* i18n references. Add
useTranslation import and hook call to 15 components that lacked it.
Zero hardcoded aria-labels remain in the codebase.

* feat(a11y): add aria-labels to icon-only buttons, aria-hidden on decorative icons, sr-only status text

* feat(a11y): add aria-live regions for processing status announcements

* feat(a11y): add skip-nav link, route announcer, main content landmark, and page h1 elements

* feat(a11y): add prefers-reduced-motion support, preserve functional spinners

* feat(a11y): add useFocusTrap hook for modal focus management

* feat(a11y): add focus trapping and dialog roles to all modals

* feat(a11y): add toggle switch roles, form labels, and error association

* fix(a11y): fix contrast failures, touch targets, and add nav landmark to sidebar

* fix(a11y): add role=switch to remaining toggle buttons found in verification sweep
2026-06-07 23:32:41 +08:00
SnapOtterandGitHub 73b259462a fix: resolve 7 bugs from QA sweep (#208)
- Fix selective metadata stripping (P1): use Sharp's keepExif()/keepIccProfile()
  instead of broken withMetadata({}) that preserved everything
- Fix meme font mapping (P1): add ArchivoBlack and ComicNeue fonts, map
  arial-black and comic-sans to correct TTF files instead of Anton
- Fix meme contentType (P2): detect actual output format from Sharp metadata
  instead of hardcoding image/png
- Fix info/text-overlay/color-palette i18n (P2): wire up existing translation
  keys that were imported but never used
- Fix info and color-palette displayMode (P2): change from before-after to
  no-comparison since neither tool produces a processed image
- Add missing i18n keys across all 21 locales
- Update displayMode test assertions
2026-06-07 18:27:09 +08:00
SnapOtter 6b037e3abc feat: add html file upload mode to html-to-image tool 2026-06-06 21:45:39 +08:00
SnapOtter af1220cab1 feat: add frontend store and components for html-to-image tool 2026-06-06 21:45:38 +08:00
SnapOtter fe0cbd32b2 fix: use correct i18n key path for noToolsFound in tool palette 2026-06-06 10:38:34 +08:00
SnapOtter 91c66eb4be fix: resolve all QA report issues from 2026-06-05 sweep
Merges 77 QA issue fixes across routing, editor, i18n, pipeline,
and settings. Includes catch-all 404 page, keyboard shortcuts,
export filename, password validation, selection masking, brush flow,
eraser modes, feather control, pipeline persistence, RTL properties,
license info, and i18n for all 21 locales.
2026-06-06 10:37:36 +08:00
SnapOtter 37f8ebcf66 Merge branch 'feat/remove-bg-improvements' 2026-06-05 23:05:33 +08:00
SnapOtter 80957f6e10 feat: improve remove background with edge smoothing, color decontamination, output formats
- Expose birefnet-hr-matting in UI (People/Ultra) and fix model defaults
  (People/Max now uses birefnet-matting for true alpha matting)
- Add output format selector (PNG/WebP/AVIF) with lossless alpha support
- Add edge smoothing post-processing (Off/Light/Medium/Strong) via
  morphological mask refinement to reduce gray halo artifacts
- Add color decontamination to remove background color spill from
  semi-transparent edge pixels
- Thread new settings through full stack: frontend -> API schema ->
  Python sidecar -> Sharp effects pipeline
- Add i18n keys for all 21 locales
- Add unit tests for new option serialization (3 tests)
- Add integration tests for new settings validation (4 tests)
2026-06-05 23:05:25 +08:00
SnapOtter 07e12754ba fix: resolve QA report issues across routing, editor, i18n, and pipeline
- Add /tools/:toolId legacy redirect and catch-all 404 page (P1-12, P1-13)
- Add Shift+O dodge/burn/sponge cycle and Ctrl+Y redo shortcut (P1-3, P1-4)
- Fix Fit on Screen menu action to properly compute fit zoom (P1-8)
- Add filename input to editor export dialog (P1-2)
- Fix password validation mismatch: frontend now requires 8 chars (P1-11)
- Add license info to Settings About section (P1-9)
- Replace hardcoded strings in dropzone, files, pipeline with i18n keys (P1-17 to P1-25)
- Add 20+ missing i18n keys to all 21 locale files
- Translate Japanese editor.shapes and settings.aiFeatures sections (P1-19, P1-20)
- Fix RTL: use logical CSS properties in sidebar, files, app-layout (P2-24 to P2-26)
- Add single-file download button to pipeline results (P1-28)
- Fix compress step settings restoration on pipeline load (P1-27)
- Increase mobile nav touch targets to 44px minimum (P2-29)
2026-06-05 22:31:28 +08:00
SnapOtter 6a8d9dcd8d feat: implement 6 research-backed mobile responsive features
- Bottom sheet for mobile tool settings (replaces top-collapsible panel)
- Pinch-to-zoom and wheel zoom on image viewer via @use-gesture/react
- Replace all vh units with dvh for dynamic viewport height
- Vertical before-after comparison on mobile devices
- Konva multi-touch pinch-to-zoom on editor canvas
- Container queries for adaptive tool settings + touch-friendly CSS
2026-06-05 22:30:43 +08:00
SnapOtter 228f70d011 feat: add static demo site at demo.snapotter.com
- New apps/demo/ that reuses apps/web components with mocked API layer
- Full UI shell: login, change password, analytics consent, dashboard, all tool pages
- Stateful mock tracks session flow (password change, analytics consent)
- Demo banner with link to GitHub repo
- Processing attempts show info message with GitHub link
- Deployed to Cloudflare Pages as static site (no backend)

Also links demo across all surfaces:
- README: "Live Demo" badge
- Landing navbar: "Try Demo" CTA button (replaces "Book a Demo")
- Landing hero: "No sign-ups. No credit card." tagline
- Docs getting-started: "Try before installing" tip box

Other changes:
- Docs: move NVIDIA GPU section above GHCR, demote GHCR to collapsed details
- Fix before-after slider checkerboard background for transparency
- Fix remove-bg preview reset when no effects applied
2026-05-17 09:25:12 +08:00
SnapOtter d38621d7b9 feat: add multi-language support for 20 locales
Add complete i18n infrastructure with 21 supported languages:
English, Simplified Chinese, Traditional Chinese, Japanese, Korean,
Spanish, French, Italian, Brazilian Portuguese, German, Dutch, Swedish,
Russian, Polish, Ukrainian, Arabic (RTL), Turkish, Hindi, Vietnamese,
Indonesian, and Thai.

- I18nProvider context with three-tier locale detection
  (user preference > navigator.languages > instance default > English)
- ~1500 translation keys per locale with TypeScript-enforced completeness
- Dynamic code-splitting: only the active locale is loaded at runtime
- Language selectors in footer, login page, settings, and mobile sidebar
- Arabic RTL support with CSS logical properties across all components
- Tool names, descriptions, and categories translated via i18n helpers
- Public API endpoint GET /api/v1/config/locale for instance default
- Multi-script font stack (CJK, Arabic, Devanagari, Thai, Cyrillic)
- format() and plural() helpers for interpolation and pluralization
- API error translation mapping (translateApiError)
- 36 Playwright e2e tests verifying all 21 locales load correctly
- 25 unit tests for format, plural, locale detection, and completeness
- Updated translations.md docs and CLAUDE.md conventions
2026-05-15 17:02:49 +08:00
SnapOtter c6a5d3f33e refactor: remove content-aware-crop tool
Remove the content-aware-crop tool entirely -- API route, frontend
settings component, e2e and integration tests, and all registry
entries.
2026-05-13 17:55:01 +08:00
SnapOtter 443e9a4ffa fix: replace broken LaMa watermark detection with median filter approach
The luminance anomaly detection + LaMa inpainting approach failed because
watermark signal on the matted foreground was too weak (10-15 units vs
threshold of 25). Median filter with kernel=5 effectively removes
semi-transparent watermark text while preserving the stamp structure.

Pipeline is now: median filter (if toggle on) -> BiRefNet matting -> defringe.
No longer requires object-eraser-colorize bundle for watermark removal.
2026-05-13 17:08:15 +08:00
SnapOtter 6f30dbc09e feat(web): remove mode selector, add colorize strength slider 2026-05-13 17:06:45 +08:00
SnapOtter 54f3867086 feat: add Remove Watermark toggle UI with feature gating and e2e tests 2026-05-13 16:20:40 +08:00
SnapOtter c3dc98834d feat: add quality tier selector to AI canvas expand UI 2026-05-13 15:47:58 +08:00
SnapOtter 0b3f46407a fix: convert HDR/EXR to 8-bit before CLAHE in image enhancement
HDR and EXR files decoded by ImageMagick can produce 16-bit PNG buffers.
Sharp's CLAHE operation (hist_local) requires VIPS_FORMAT_UCHAR (8-bit).
Check the buffer depth and convert to 8-bit sRGB before processing.
2026-05-13 14:16:18 +08:00
SnapOtter b413ace031 feat: multi-file eraser with sequential processing and Erase All button 2026-05-13 13:43:13 +08:00
SnapOtter 09d494d87d feat: store eraser strokes per-image for multi-file support 2026-05-13 13:32:44 +08:00
SnapOtter 3497cd0c9a fix: show checkerboard pattern for transparent background in remove-bg tool 2026-05-13 12:10:01 +08:00
SnapOtter d22bebb8ad fix: gracefully handle mixed formats in find-duplicates and fix network errors
The find-duplicates tool failed entirely when any uploaded file couldn't
be processed, returning "Duplicate detection failed" or a format-specific
error that aborted the whole batch. With mixed-format uploads (77 files),
this made the tool unusable.

- Skip unprocessable files instead of aborting; return skippedFiles in response
- Switch from fetch() to XHR with upload progress tracking (Uploading X%)
- Add Vite proxy timeout config (5min) to prevent connection drops on large uploads
- Add "Download Grouped" button: ZIP with each duplicate group in its own folder
- Add collapsible skipped-files section in the results UI
- Add 3 integration tests for skip behavior (43 total)
2026-05-13 11:35:33 +08:00
SnapOtter abfad0de56 fix: progress bar not showing during stitch processing
setError() in the file store unconditionally set processing to false,
even when called with null to clear a previous error. This meant
setProcessing(true) followed by setError(null) at the start of
processing would immediately kill the processing state, preventing the
progress bar from ever rendering.

Fix setError to only reset processing when there is an actual error.
This also fixes the same latent bug in 10+ other custom tool settings
components that call setProcessing before setError.
2026-05-13 11:04:35 +08:00
SnapOtter 8637105bce fix: add exotic format decoding to image-to-pdf tool 2026-05-13 10:39:38 +08:00
SnapOtter f93e864094 fix: add progress bar and batch download to vectorize tool
The vectorize tool had a custom processing flow that bypassed the
standard useToolProcessor hook -- no progress indication, no server-side
batch, and the Download All ZIP relied on a client-side sequential loop.

Backend: extract core logic into vectorizeBuffer(), register via
registerToolProcessFn() so the /batch endpoint works with p-queue
concurrency and SSE progress events.

Frontend: replace custom fetch loop with useToolProcessor hook and
ProgressCard, giving upload progress, per-file batch status, and
automatic Download All ZIP via the existing tool-page infrastructure.

Also set image/svg+xml MIME type on SVG blobs during batch ZIP
extraction to ensure reliable rendering in <img> tags across browsers.
2026-05-13 10:36:35 +08:00
SnapOtter bf01dcd47e fix: make beautify live preview reflect all settings in real time
Background images, device frames, custom shadows, and watermark text
were not rendering in the right-pane preview. The preview now updates
in real time for all settings: gradient/solid/image backgrounds, macOS/
Windows/Browser frame chrome, iPhone/MacBook/iPad frame indicators,
custom shadow parameters, and watermark text overlay.

Also fixes a React StrictMode effect-ordering race where the parent
tool-page reset cleared preview state set by the child Settings
component on initial mount.
2026-05-13 10:28:44 +08:00
SnapOtter 0874cfc809 fix: add exotic format decoding and upload progress to stitch tool
The stitch route only decoded HEIC via ensureSharpCompat, so exotic
formats (DNG, PSD, TGA, BMP, JXL, HDR, QOI, DDS, ICO, JP2, DPX, etc.)
crashed Sharp at metadata read time, causing "Stitch creation failed".
Add the full CLI decode pipeline (decodeToSharpCompat) matching the
tool-factory pattern, plus SVG sanitization with proper error handling
for each format category.

Replace fetch() with XMLHttpRequest in the frontend to surface upload
progress via onprogress events, and add a progress bar that shows upload
percentage then pulses during server-side stitching.
2026-05-13 10:27:59 +08:00
SnapOtter 7a3ec93847 fix: clear error when navigating to cached image info and pass RAW extension
Error from a failed image (e.g. DNG) persisted when navigating to a
previously cached image because the cache path skipped setError(null).
Also pass the original file extension to decodeToSharpCompat so RAW
variants get the correct temp file suffix for ExifTool/ImageMagick.
2026-05-12 21:02:35 +08:00
SnapOtter 449f5dc97e fix: add exotic format decoding to image composition tool
The compose route only decoded HEIC/HEIF via ensureSharpCompat, causing
EPS, PSD, BMP, RAW, and other exotic formats to fail with
"Processing failed". Now uses the same full format pipeline as the
tool-factory for both base and overlay buffers.
2026-05-12 19:41:23 +08:00
SnapOtter d3d50c92be fix: update image info panel when navigating between files
Info panel always read files[0] and stored results in a single useState,
so it never changed when switching images. Now tracks selectedIndex,
auto-fetches on navigation after initial "Read Info" click, caches
results per-index, and aborts stale requests on rapid navigation.
2026-05-12 18:40:33 +08:00
SnapOtter 7a2ea2069b fix: clean up crop mode toggle, extend timeouts for long-running tools
- Remove content-aware crop mode toggle from tool-page (now handled internally)
- Add content-aware-resize/crop to LONG_RUNNING_TOOLS for extended XHR timeout
- Remove unused onModeChange prop from CropProps
- Bump HEIC converter test timeouts to 60s to fix flaky failures
2026-05-11 22:48:05 +08:00
SnapOtter b06906025c refactor: improve tool processing, dropzone, seam carving, and format encoding
- Refactor use-tool-processor and use-pipeline-processor hooks
- Enhance dropzone component with improved UX
- Improve seam carving with better error handling and tests
- Add JXL format encoding support to format-encoders
- Update tool routes for consistent format handling
- Add dropzone unit tests
2026-05-11 21:57:40 +08:00
SnapOtter 9763a10db4 fix: add tool registry entry and manifest for content-aware-crop 2026-05-11 21:23:47 +08:00
SnapOtter 8ed0700a04 feat: add Content-Aware tab to crop settings with expansion controls 2026-05-11 21:15:03 +08:00
SnapOtter 50d1f532d1 fix: block PDF uploads, fix PBM/PGM/PPM batch failures, add tests
- Added isImageFile() filter to all drop handlers (dropzone, collage,
  file-upload-area) so PDFs and non-image files are rejected on drop.
  Previously only the file picker's accept attribute filtered; drag-and-
  drop accepted anything.

- Added ppm, pgm, pbm to CLI_DECODED_FORMATS in file-validation.ts.
  These formats were missing, causing Sharp metadata checks to fail for
  some files during batch validation, which silently dropped them from
  results ("File not found in batch results").

- Added integration tests for PBM, PGM, PPM, TIFF, QOI, JP2, SVGZ
  single-file processing, plus a test confirming PDF is rejected.
2026-05-11 17:33:51 +08:00
SnapOtter 0f1fd2050b fix: add +/- stepper buttons to quality slider in compress tool
Small bordered buttons with Minus/Plus icons flank the range slider,
allowing single-step quality adjustments. Disabled at min (1) and
max (100) boundaries.
2026-05-11 17:12:24 +08:00
SnapOtter aec1e91c5a fix: add .avif to all file picker accept strings
AVIF was missing from expandAccept() and every inline accept attribute,
causing the OS file picker to gray out .avif files. Drag-and-drop was
unaffected because it bypasses the accept filter. Added .avif to
dropzone.tsx expandAccept (covers all Dropzone pickers) plus 12 inline
accept strings across tool settings, pages, and file-upload-area.
2026-05-11 16:59:49 +08:00
SnapOtter 72de10b844 fix: clean up files section and add KB/MB unit selector to compress
- Remove duplicate "Files" heading (section header + inline count)
- Space "Show more" and "Clear all" apart on the same row
- Replace bare number input with number + KB/MB dropdown for target
  size, defaulting to KB; MB values are converted to KB for the API
- Hide native number input spin buttons for a cleaner look
2026-05-11 16:54:13 +08:00
SnapOtter 5b868886ba fix: make target size the primary compression mode
Move Target Size button to the left and default to it on load, since
it is the more useful mode for most users.
2026-05-11 16:33:10 +08:00
SnapOtter ee871dc8ea feat: add Deep Enhance toggle to image enhancement UI
Toggle sends deepEnhance: true in settings to enable AI-powered
noise/artifact cleanup via SCUNet after the Sharp pipeline.
2026-05-09 11:31:36 +08:00
SnapOtter bdcfb6647c fix: sync frontend preset multipliers and fix sharpen type error
Add clahe and normalise entries to frontend PRESET_MULTIPLIERS to match
backend. Fix SharpenOptions to use m1 (not flat) per Sharp 0.34 types.
2026-05-09 11:26:49 +08:00
SnapOtter aa773addb4 merge: resolve conflict with main branch in tool-registry.tsx 2026-05-08 18:55:28 +08:00
SnapOtter dac30f14f0 fix(meme-generator): match preview and server render text sizing
- 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
2026-05-08 18:17:17 +08:00