Commit Graph
1117 Commits
Author SHA1 Message Date
SnapOtterandGitHub aa3ae6ec91 fix(web): show modality thumbnails for non-image files in the strip (#245)
The multi-file thumbnail strip rendered an <img> with the file blob URL
for every entry, so audio, video, and document files showed a broken
image icon with the filename as alt text instead of a preview.

Render an <img> only when there is a real image to show (a processed
preview, a processed image output, or an image-modality original).
Otherwise show a modality icon (waveform / film / document) plus the
file's format label, using the entry's existing previewKind.
2026-06-16 14:33:27 +08:00
SnapOtterandGitHub 03e71236f9 fix: validate non-image inputs by modality (batch + pipeline) (#244)
* fix(batch): validate non-image inputs by modality

Batch processing ran validateImageBuffer and the image-only decode chain
on every uploaded file, so audio, video, and document tools rejected all
inputs with "Invalid image: Unrecognized image format" and returned
422 "All files failed processing".

Resolve the tool's modality and route non-image files through their own
input handler (inputHandlerFor(modality).prepare), mirroring the
single-file path. The image batch path is unchanged.

* fix(pipeline): validate non-image inputs by modality

The pipeline /execute and /batch routes validated every upload with
validateImageBuffer and ran the image-only decode chain, so audio,
video, and document pipelines were rejected with "Invalid image:
Unrecognized image format".

Resolve the input modality from the first step's tool and route
non-image inputs through their modality handler, mirroring the batch
and single-file paths. Image pipelines are unchanged.
2026-06-16 14:33:23 +08:00
SnapOtterandGitHub d8cf979d4b fix: resolve 18 QA-discovered bugs across tools, previews, and the AI pipeline (#242)
Exhaustive QA sweep of all 157 tools. Fixes: CSP blob media, csv-excel ExcelJS interop, ocr-pdf segfault, chart-maker upload, non-PDF doc preview, RAW decode, merge-tool multi-file path, html-to-image chromium, ogv/wma/amr/ac3 preview fallbacks, meme/gif/stabilize codecs, nav+home a11y. Plus orphan-format and test-debt cleanup, the AI bundle build script, and a reusable Playwright QA harness under tests/qa/.
2026-06-15 22:26:24 +08:00
SnapOtterandGitHub f622a4f691 feat(landing): enterprise-focused hero redesign and section polish (#239)
Reframe the landing page around the self-hosted enterprise buyer.

Hero
- Rotating-modality headline with a fixed "Nothing leaves your network"
  promise (pure CSS, no client JS) and an enterprise subtitle
- Instant, modality-aware tool search (multi-word) linking to per-tool pages
- Modality cards with live counts; stat strip (150+ tools, GitHub stars,
  100K+ image pulls, 20+ languages); 7 enterprise trust badges
- Removed the Docker terminal and the hero CTAs

Sections
- "Built for enterprise deployment" redesigned as a security control plane
  (category-tagged 4-up grid, audit-ready frameworks row)
- "Built for regulated environments" polished with numbered kickers and an
  accurate Docker Compose deployment description
- ToolGrid is browse-only now (dropped its redundant search) with a clearer heading

Fixes and infra
- Per-tool pages render the real tool icon instead of a placeholder square
- Navbar GitHub stars fetch live client-side with a 1-hour localStorage cache
- Stats auto-update from the GitHub and Docker Hub APIs at build time
- Enterprise tab title and meta description
2026-06-15 21:55:00 +08:00
SnapOtter b76dc68682 fix: resolve Sharp 0.35.1 and BullMQ type incompatibilities after dep bumps
Sharp 0.35.1 moved FormatEnum to a namespace export and removed "avif"
from FormatEnum (now a separate literal in toFormat). BullMQ 5.78.1
bundles ioredis 5.10.1 while we have 5.11.1, causing structural type
mismatch. Also fixes new Biome 1.9 lint rules.
2026-06-15 15:20:16 +08:00
SnapOtter 9a61cb6af1 chore(deps-dev): bump the dev-deps group with 10 updates (#238)
Bumps @biomejs/biome, @testcontainers/postgresql, @testcontainers/redis,
@tailwindcss/vite, @types/node, @types/react, @types/yauzl, tailwindcss,
semantic-release, turbo.
2026-06-15 15:03:19 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
784f7a28cd chore(deps): bump the production-deps group with 15 updates (#237)
Bumps the production-deps group with 15 updates:

| Package | From | To |
| --- | --- | --- |
| [@scalar/fastify-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/fastify) | `1.58.0` | `1.59.3` |
| [@sentry/node](https://github.com/getsentry/sentry-javascript) | `10.56.0` | `10.57.0` |
| [bullmq](https://github.com/taskforcesh/bullmq) | `5.78.0` | `5.78.1` |
| [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) | `5.8.0` | `5.9.0` |
| [ioredis](https://github.com/luin/ioredis) | `5.10.1` | `5.11.1` |
| [pdfkit](https://github.com/foliojs/pdfkit) | `0.18.0` | `0.19.1` |
| [posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node) | `5.35.14` | `5.37.0` |
| [sharp](https://github.com/lovell/sharp) | `0.34.5` | `0.35.1` |
| [@sentry/react](https://github.com/getsentry/sentry-javascript) | `10.56.0` | `10.57.0` |
| [posthog-js](https://github.com/PostHog/posthog-js) | `1.379.2` | `1.386.6` |
| [react-colorful](https://github.com/omgovich/react-colorful) | `5.6.1` | `5.7.0` |
| [react-konva](https://github.com/konvajs/react-konva) | `19.2.3` | `19.2.5` |
| [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) | `7.16.0` | `7.17.0` |
| [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.1066.0` | `3.1068.0` |
| [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) | `3.1066.0` | `3.1068.0` |


Updates `@scalar/fastify-api-reference` from 1.58.0 to 1.59.3
- [Release notes](https://github.com/scalar/scalar/releases)
- [Changelog](https://github.com/scalar/scalar/blob/main/integrations/fastify/CHANGELOG.md)
- [Commits](https://github.com/scalar/scalar/commits/HEAD/integrations/fastify)

Updates `@sentry/node` from 10.56.0 to 10.57.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/10.56.0...10.57.0)

Updates `bullmq` from 5.78.0 to 5.78.1
- [Release notes](https://github.com/taskforcesh/bullmq/releases)
- [Commits](https://github.com/taskforcesh/bullmq/compare/v5.78.0...v5.78.1)

Updates `fast-xml-parser` from 5.8.0 to 5.9.0
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.8.0...v5.9.0)

Updates `ioredis` from 5.10.1 to 5.11.1
- [Release notes](https://github.com/luin/ioredis/releases)
- [Changelog](https://github.com/redis/ioredis/blob/main/CHANGELOG.md)
- [Commits](https://github.com/luin/ioredis/compare/v5.10.1...v5.11.1)

Updates `pdfkit` from 0.18.0 to 0.19.1
- [Release notes](https://github.com/foliojs/pdfkit/releases)
- [Changelog](https://github.com/foliojs/pdfkit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/foliojs/pdfkit/compare/v0.18.0...v0.19.1)

Updates `posthog-node` from 5.35.14 to 5.37.0
- [Release notes](https://github.com/PostHog/posthog-js/releases)
- [Changelog](https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md)
- [Commits](https://github.com/PostHog/posthog-js/commits/posthog-node@5.37.0/packages/node)

Updates `sharp` from 0.34.5 to 0.35.1
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.34.5...v0.35.1)

Updates `@sentry/react` from 10.56.0 to 10.57.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/10.56.0...10.57.0)

Updates `posthog-js` from 1.379.2 to 1.386.6
- [Release notes](https://github.com/PostHog/posthog-js/releases)
- [Changelog](https://github.com/PostHog/posthog-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/PostHog/posthog-js/compare/posthog-js@1.379.2...posthog-js@1.386.6)

Updates `react-colorful` from 5.6.1 to 5.7.0
- [Release notes](https://github.com/omgovich/react-colorful/releases)
- [Changelog](https://github.com/omgovich/react-colorful/blob/master/CHANGELOG.md)
- [Commits](https://github.com/omgovich/react-colorful/commits/5.7.0)

Updates `react-konva` from 19.2.3 to 19.2.5
- [Release notes](https://github.com/konvajs/react-konva/releases)
- [Commits](https://github.com/konvajs/react-konva/commits)

Updates `react-router-dom` from 7.16.0 to 7.17.0
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@7.17.0/packages/react-router-dom)

Updates `@aws-sdk/client-s3` from 3.1066.0 to 3.1068.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1068.0/clients/client-s3)

Updates `@aws-sdk/lib-storage` from 3.1066.0 to 3.1068.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-storage/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1068.0/lib/lib-storage)

---
updated-dependencies:
- dependency-name: "@scalar/fastify-api-reference"
  dependency-version: 1.59.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: "@sentry/node"
  dependency-version: 10.57.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: bullmq
  dependency-version: 5.78.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: fast-xml-parser
  dependency-version: 5.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: ioredis
  dependency-version: 5.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: pdfkit
  dependency-version: 0.19.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: posthog-node
  dependency-version: 5.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: sharp
  dependency-version: 0.35.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: "@sentry/react"
  dependency-version: 10.57.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: posthog-js
  dependency-version: 1.386.6
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: react-colorful
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: react-konva
  dependency-version: 19.2.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: react-router-dom
  dependency-version: 7.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1068.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: "@aws-sdk/lib-storage"
  dependency-version: 3.1068.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 15:02:25 +08:00
SnapOtter 507fc1c356 Merge branch 'feat/tool-first-workflow'
Resolved conflict in worker.ts: kept remote refactored worker
(v2 process, scratch paths, extra outputs, metrics) and re-applied
the auto-save removal from the feature branch.
2026-06-15 14:55:58 +08:00
SnapOtter 857aa75562 fix: apply processedFileName fallback everywhere processedFilename is used
Three places were using currentEntry?.processedFilename directly
without the processedFileName fallback (which parses the download URL):
- Video-to-image output detection (gif/webp check)
- Browser previewable check (canBrowserPreview)
- Non-native format detection (already fixed in prior commit)

All now consistently use processedFileName as fallback when
processedFilename is empty on the file entry.
2026-06-15 14:52:02 +08:00
SnapOtter 14ded6fbd0 fix: use processedFileName fallback for non-native format detection after processing 2026-06-15 14:49:30 +08:00
SnapOtterandGitHub 3fb8164fa5 feat: add OpenTelemetry distributed tracing (enterprise) (#232)
* feat(tracing): add OpenTelemetry dependencies and --import preload flag

* feat(enterprise): add distributed_tracing feature gate

* feat(tracing): add SDK bootstrap with enterprise gating

* fix(tracing): correct test coverage for enterprise-unavailable path and prevent double-init

Test 2 now mocks @snapotter/enterprise to throw an import error, exercising
the catch block in the preload. Test 3 imports with no endpoint so the preload
is a no-op, avoiding leaked SDK from double-initialization. Added idempotency
guard to initTracing() as a safety net.

* feat(tracing): add Pino trace mixin and shared logger

When OTel tracing is active, every Pino log line now includes traceId,
spanId, and traceFlags fields for log-to-trace correlation. The mixin
is a no-op when no SDK is registered (community users).

* feat(tracing): add _otel to ToolJobData and inject trace context at enqueue

Add optional _otel carrier field to ToolJobData for W3C trace context
propagation across BullMQ job boundaries. When an active OTel span exists,
propagation.inject() writes traceparent/tracestate into the job data before
queue.add(). When no SDK is registered (community edition), the carrier
stays empty and _otel remains undefined -- zero overhead.

* feat(tracing): extract trace context and create spans in BullMQ worker

* feat(tracing): inject trace context into Python sidecar calls

* feat(tracing): add trace context extraction to Python sidecar

* feat(tracing): add shutdownTracing to graceful shutdown sequence

* feat(tracing): enrich HTTP spans with tool_id and user_id attributes

* docs: add OpenTelemetry env var documentation to .env.example

* test(tracing): add lifecycle integration tests for trace propagation

* fix(tracing): inject trace context into pipeline and batch flow jobs

* fix(tracing): add sidecar.execute Node-side span and remove unnecessary comment

Wraps PythonDispatcher.run() with a sidecar.execute span on the Node
side so traces show the full round-trip (Node span -> Python span).
Also removes an obvious comment from logger.ts.
2026-06-15 12:53:06 +08:00
SnapOtter abaae18669 fix: always show NonNativePreview for non-native formats, even after processing 2026-06-15 12:41:42 +08:00
SnapOtter b93dc95bca fix: preview generation works for processed results too
NonNativePreview now accepts either a File (pre-processing) or a src
URL (post-processing). After processing a non-native video (e.g.,
muting an AVI), the preview button fetches the processed result from
its URL and sends it for transcoding, instead of showing the broken
MediaPlayerView.
2026-06-15 12:21:12 +08:00
SnapOtter 6090688708 fix: send auth token with preview generation request 2026-06-15 12:17:26 +08:00
SnapOtter d3cd4e41b5 feat: on-demand preview generation with progress messages for non-native formats 2026-06-15 12:04:30 +08:00
SnapOtter 511e941da0 fix: clean file info cards for non-native video/media formats on tool page
Instead of showing a broken video player error for formats like AVI,
MKV, WMV that browsers can't play, show a clean file info card with
icon, filename, format, and size. Message says "Preview will be
available after processing" which is accurate -- the processed output
is typically in a browser-friendly format.

Also cleaned up the post-processing non-previewable card to match
the same visual style.
2026-06-15 11:57:27 +08:00
SnapOtter ed458d4bbd feat: redesign review panel action hierarchy
- Download stays primary (full-width button)
- Save to Files demoted to subtle text link below download
- "Adjust settings" and "New file" are side-by-side compact buttons
- "Back to Tools" faded to very subtle (breadcrumb handles this)
- Clearer visual hierarchy: primary -> secondary -> tertiary actions
2026-06-15 11:52:22 +08:00
SnapOtter e7126f9293 feat: All tab groups tools by modality with collapsible sections
The All tab now shows tools organized by modality (Image, Video,
Audio, Documents, Data) with each section showing a colored icon,
tool count, and collapse toggle. Categories are nested within each
modality. Collapse state persists in localStorage so enterprise
users can hide modalities they don't use.

Modality tabs still show the flat category view for focused browsing.
2026-06-15 11:47:58 +08:00
SnapOtter 52693fc843 feat: arrow key navigation in Files list
ArrowUp/ArrowDown to move between files, Home/End to jump to
first/last. Selected file scrolls into view automatically.
List container is focusable and keyboard-navigable.
2026-06-15 11:42:59 +08:00
SnapOtter 3decfaae3e feat: clean preview button with progress bar, add document preview support 2026-06-15 11:41:11 +08:00
SnapOtter 412a21ee4d feat: server-side preview generation for non-native video/audio formats 2026-06-15 11:33:45 +08:00
SnapOtter a70b13ad32 feat: modality-aware file preview in Files library
- Video files: inline video player with controls
- Audio files: waveform visualization with playback (wavesurfer.js)
- Image files: thumbnail preview (existing behavior)
- Documents/data: file type icon with format label
2026-06-15 11:14:51 +08:00
SnapOtter 3a5d55cf26 fix: grey out incompatible files in library instead of hiding them
When importing from Files for a specific tool, all files are shown
but incompatible ones (wrong modality) are greyed out and unclickable.
This gives users full visibility of their library while making it
clear which files can be used.
2026-06-15 11:10:35 +08:00
SnapOtter adfa53246a fix: enforce file type filtering using tool's acceptedInputs
When registryEntry.accept is undefined, fall back to tool.acceptedInputs
to derive the file filter. This ensures image tools reject audio/video
files in the file picker, and vice versa.
2026-06-14 23:42:20 +08:00
SnapOtter 826f1efc08 feat: filter files by modality when importing from tool page
When clicking "Import from Files" on an image tool, only image files
are shown in the library. Video tools see only videos, audio tools
only audio. Uses MIME type prefix filtering (image/, video/, audio/).
Document and data tools see all files since their MIME types vary.
2026-06-14 23:26:38 +08:00
SnapOtter aff9159ae0 feat: move theme toggle and language selector to top nav bar
Theme toggle (sun/moon) and language selector (globe + dropdown) now
live in the top nav's right section alongside help and avatar.
Removed the floating Footer component from the home page since its
contents are now in the nav bar.
2026-06-14 23:13:38 +08:00
SnapOtter 6bcee1e073 fix: file library upload accepts all file types, not just images
The upload endpoint was rejecting non-image files via validateImageBuffer.
Now non-image files skip validation and use the MIME type from the
multipart upload. Width/height are null for non-image files.
2026-06-14 23:11:38 +08:00
SnapOtter f4061fe4b0 fix: Save to Files shows green success state with checkmark after saving 2026-06-14 23:10:20 +08:00
SnapOtter d0795b69ea feat: disable auto-save, add Save to Files button, accept all file types in library
- Disabled auto-save of processed files to library (worker no longer
  calls autoSaveToLibrary)
- Added "Save to Files" button in the review panel for explicit saving
- Files library upload area now accepts all file types, not just images
- Removed "Drop images here" hardcoded text, replaced with i18n
- Removed image-only file filter from library upload
2026-06-14 21:53:42 +08:00
SnapOtter b8a6b2018e feat: Import from Files works, remove pipeline button from files page
- Import from Files now navigates back to the tool page with the
  selected file loaded (uses selectForTool router state)
- Tool page skips file store reset when arriving from the library
- Removed "Open in Pipeline" button from file details and file list
- Button text changes to "Select File" when in file-selection mode
2026-06-14 21:43:58 +08:00
SnapOtter b5eef644e3 feat: clean tool page nav, add Import from Files on dropzone
- Removed Automate/Editor/Files links from top nav on tool pages,
  keeping only the breadcrumb + help + avatar for a cleaner look
- Added "Import from Files" link on the tool dropzone that navigates
  to the file library, letting users import previously processed files
2026-06-14 21:37:15 +08:00
SnapOtter f32b3bd51f fix: clear file store when navigating between tools
Changed undoProcessing() to reset() on toolId change so files don't
persist when navigating from one tool to another. Previously a video
file from a video tool would carry over to an image tool.
2026-06-14 21:32:27 +08:00
SnapOtter 90acd5bcd9 fix: show image viewer for video-to-gif/webp results instead of broken video player
When a video tool produces a non-video output (gif, webp, png), the
media player can't render it. Now detects the processed file extension
and switches to ImageViewer for image outputs while keeping the video
player for the input preview.
2026-06-14 21:30:54 +08:00
SnapOtter 69300b5c83 fix: use media-player for all video/audio tools that had no-comparison or side-by-side
video-to-webp, video-to-frames, auto-subtitles, extract-subtitles,
video-metadata, and audio-metadata were using display modes that
render via ImageViewer, which can't display video/audio files
(shows "Preview not available"). Changed to media-player so they
get proper video player or waveform visualization.
2026-06-14 21:27:26 +08:00
SnapOtter d8a8e05590 feat: add audio waveform visualization with wavesurfer.js
Replace the basic HTML5 audio element with an interactive waveform
player for audio tool pages. Video tools continue using the existing
MediaPlayerView component.
2026-06-14 21:25:34 +08:00
SnapOtter 5241ccd124 fix: show tool-specific accepted formats in dropzone, remove privacy note
The inner Dropzone was showing generic "Images, Videos, Audio, PDFs..."
text. Now it shows the tool's actual accepted formats (e.g., "MP4, MOV,
WEBM, MKV..." for video tools) via the acceptDescription prop.
Removed the privacy note text per user feedback.
2026-06-14 21:18:45 +08:00
SnapOtter 207c1c348b fix: make search bar full-width to align with tabs and grid 2026-06-14 21:09:42 +08:00
SnapOtter 43b190134e fix: top nav respects dark mode theme
Changed bg-white to bg-background so the top nav bar adapts to the
app's dark/light theme via CSS variables instead of being hardcoded
to white.
2026-06-14 21:08:34 +08:00
SnapOtter ade79c53a5 feat: polish home page UI -- card borders, colored icons, 3-col grid, Cmd+K hint
- Tool cards: subtle border, modality-colored icon backgrounds, better hover
- Category headers: bottom border separator for visual structure
- Grid: 3 columns on lg screens instead of 2
- Search bar: Cmd+K keyboard shortcut hint, tighter styling
- Tabs: softer styling, count separated from label
- Recent pills: subtler border style instead of bright orange
- Tighter vertical spacing throughout
- Wider max-width (max-w-6xl) for better use of screen space
2026-06-14 21:03:52 +08:00
SnapOtter fd26fa3586 fix: remove all legacy redirects from routes
Removed /fullscreen redirect, old color tool redirects, and legacy
/:toolId redirect. Clean route table with only active routes.
2026-06-14 20:56:46 +08:00
SnapOtter ea83e1c8b4 feat: modality-based URLs (/image/resize, /video/compress-video)
Tool routes now include the modality prefix: /image/crop, /video/trim-video,
/audio/convert-audio, /document/merge-pdf, /data/csv-json. Old URLs
(e.g. /resize) redirect to the new modality-prefixed path automatically.

Routes are computed by prepending MODALITY_URL_SLUG to each tool's route
in the TOOLS array at module init. The App.tsx route changed from
/:toolId to /:modality/:toolId, with a LegacyToolRedirect fallback.
2026-06-14 20:54:29 +08:00
SnapOtter 914ff1b1aa fix: prevent double scrollbar by locking html/body overflow
Added overflow: hidden to html and body, height: 100% to html/body/#root.
Only the <main> element inside AppLayout scrolls. This prevents the
browser's default body scroll from creating a second scrollbar alongside
the app's own scroll container.
2026-06-14 20:47:19 +08:00
SnapOtter ae959760e3 fix: remove double scrollbar on home page
Main element in AppLayout now handles scrolling (overflow-y-auto).
Removed redundant overflow-y-auto from home page wrapper.
2026-06-14 20:44:17 +08:00
SnapOtter b166f47de5 fix: show all tools on All tab, remove Popular and Browse sections
All tab now shows the full tool list grouped by category (same as
modality tabs). Removed Getting Started, Popular, and Browse by
Category sections for a simpler, direct experience.
2026-06-14 20:40:45 +08:00
SnapOtter 287f6f15de fix: remove redundant Getting Started section from home page
The Getting Started section duplicated tools already shown in
Popular. Now first-time users (no recents) go straight to Popular
and Browse by Category.
2026-06-14 20:37:11 +08:00
SnapOtter cf543f6976 fix: fetch settings on home page mount so tools are visible
The old home page called fetchSettings() on mount. The rewrite
dropped that call, so the settings store never loaded and
visibleTools was always empty (0 tools shown).
2026-06-14 20:34:59 +08:00
SnapOtter f68fcb8ca6 fix: cache useSyncExternalStore snapshot to prevent infinite re-render
getSnapshot() was calling JSON.parse() on every invocation, returning
a new array reference each time. useSyncExternalStore uses Object.is
comparison, so it saw a "new" value on every render and triggered
an infinite update loop. Fix by caching the parsed result and only
re-parsing when the raw localStorage value changes.
2026-06-14 20:31:49 +08:00
SnapOtter f387e98fff feat: tool-first workflow polish -- fix tests, lint cleanup
- Remove obsolete fullscreen redirect test (route deleted in Task 3)
- Update dropzone test strings to match new i18n values
- Fix categories count in i18n locale test (12 -> 24)
- Add chart-maker to no-dropzone exempt list in tool-registry test
- Auto-fix import ordering (Biome) in popular tools route and landing config
2026-06-14 19:44:58 +08:00
SnapOtter bc3ad2b008 feat: copy-primary for data tools, download-all label for multi-output, batch failure display 2026-06-14 19:38:47 +08:00
SnapOtter 896acda7ed feat: mobile layout for tool-first flow 2026-06-14 19:32:04 +08:00