Commit Graph
2079 Commits
Author SHA1 Message Date
SnapOtterandGitHub c706a9e8a1 docs(deploy): pin pg_isready to POSTGRES_DB and flag the default password (#606)
Add -d snapotter to the guide's Compose healthcheck examples so they match
the shipped compose fix (#595), and note "change this" next to the default
POSTGRES_PASSWORD. English guide only; locale docs regenerate through the
i18n pipeline.

Refs #592
2026-07-21 18:35:24 +08:00
SnapOtterandGitHub d43208b85e fix(ai): warn that upscale and background removal are slow without a GPU (#605)
Add a one-line notice to the upscale and background-removal settings that
heavy AI runs much slower without a GPU and a large image can take minutes,
so a CPU-only self-hoster isn't caught off guard by a slow run. Translated
into all 21 locales.

Refs #591
2026-07-21 17:55:11 +08:00
SnapOtterandGitHub 89d75853f4 fix(download): ask reverse proxies not to buffer file downloads (#604)
Send X-Accel-Buffering: no on file download responses so nginx and
compatible reverse proxies stream them through instead of buffering, the
usual cause of a self-hosted download that "starts but never finishes".
The app already delivers exactly Content-Length bytes; a new real-socket
test proves it for both the collated PDF and the multi-file ZIP.

Refs #590
2026-07-21 17:02:12 +08:00
SnapOtterandGitHub e7ffb37e98 feat(image): add rounded-square and squircle crop tool (#602)
Adds a Rounded Crop image tool for logo, favicon, and app-icon work. It masks the framed square to a rounded rectangle (with a corner-radius control) or an iOS-style squircle, reusing circle-crop's zoom/offset framing, border ring, background fill, and output-size options. Includes translations across all 21 locales.

Closes #601
2026-07-21 16:48:48 +08:00
SnapOtterandGitHub 7d37f6e6f5 fix(pdf): flag scanned PDFs in pdf-to-text and serve text as UTF-8 (#603)
When a PDF has no text layer (scanned or image-only), pdf-to-text now
returns a 422 that points at the OCR tool instead of a silent empty file,
and text downloads carry charset=utf-8 so UTF-8 Arabic renders correctly
when the .txt is viewed inline.

Fixes #589
2026-07-21 16:26:43 +08:00
SnapOtterandGitHub 43334324c4 fix(api): contain library stored-name path traversal (#600)
The library file-storage helpers joined FILES_STORAGE_PATH with a database stored_name and never checked containment, so a crafted name could read or delete files outside the storage root after a malicious 1.x SQLite import (which copies stored_name verbatim). Add assertSafeStoredName() and apply it in every helper that resolves a stored name to a path, matching the containment guard object-storage already uses.

Reported by Alpesh Bhagwatkar.
2026-07-21 07:30:46 +00:00
SnapOtterandGitHub 37c915c3da fix(jobs): make timeout messages tool-agnostic and CPU-aware (#596)
Replace the job timeout message that hardcoded "background-removal" for
every tool with a tool-agnostic one that sets the CPU-vs-GPU expectation,
the usual reason heavy AI times out on modest hardware. The client-side
SSE stall message gets the same treatment. Both stay under friendlyError's
280-char limit so the guidance reaches the user instead of collapsing to a
generic "Processing failed".

Refs #591
2026-07-21 15:22:57 +08:00
SnapOtterandGitHub e6718ada71 fix(seo): emit clean docs URLs and de-index the demo (#598)
The docs sitemap listed .html URLs that Cloudflare Pages 308-redirects to
their clean form, while every page's canonical/hreflang already pointed at
the clean URL. Google indexed the redirecting .html variant and picked its
own canonical (GSC "Duplicate, Google chose different canonical"), and burned
crawl budget re-fetching redirecting URLs. Setting VitePress cleanUrls:true
makes the sitemap and internal links extension-less so they match the
canonicals. CF Pages already serves the clean URL at 200 and 308s the .html
form, so no hosting change is needed and legacy .html hits consolidate.

Also mark demo.snapotter.com noindex: it mirrors the real app under the same
domain property, so its routes were being crawled and indexed as thin,
duplicate pages.
2026-07-21 15:22:15 +08:00
SnapOtterandGitHub 577d74bdb1 fix(api): enforce job ownership on cancel endpoint (#599)
The job cancel endpoint authenticated the caller but never checked that the job belonged to them, so any authenticated user could cancel another user's job by ID. Load the job's owner and allow cancellation only for the owner or a caller with files:all; return 404 for missing and non-owned jobs alike. Extract the route into a shared registerJobRoutes() so the ownership check is covered by tests.

Reported by Alpesh Bhagwatkar.
2026-07-21 15:09:00 +08:00
SnapOtterandGitHub a6bce6825a fix(editor): repair rotate/flip/resize, levels/curves, filters, and layer lock (#597)
Fixes rotate/flip/image-size raster ops, wires Levels/Curves into the render pipeline, corrects Posterize/Threshold, seeds visible Filter-menu defaults, enforces layer lock, refreshes the histogram on edits, stops revoking in-history blob URLs, and removes the dead selection Feather control. Verified: editor e2e 168/168, web typecheck + biome clean, 226 editor unit tests pass.
2026-07-21 14:51:59 +08:00
SnapOtterandGitHub 73df107758 fix(pdf): stop page tools failing on short and encrypted PDFs (#594)
Empty the hardcoded page-range default in remove/split/extract PDF tools (remove-pages defaulted to "2,4-6", out of range for any PDF under 6 pages) and disable submit until a range is entered. Reject password-protected PDFs up front for PDF-only tools with guidance to unlock first, instead of failing cryptically in the qpdf worker. Adds integration + e2e coverage.
2026-07-21 06:14:43 +00:00
SnapOtterandGitHub 4ba7503f15 fix(docker): harden Postgres readiness checks in compose and startup (#595)
Pin the compose Postgres healthchecks to POSTGRES_DB (pg_isready was
defaulting to the username, silently reporting healthy while spamming
FATAL logs when USER and DB differ), and make docker/wait-for-postgres.mjs
log the target host and error code instead of a silent retry loop. Adds a
change-me note next to the default password in README and Docker Hub.

Refs #592
2026-07-21 14:01:02 +08:00
SnapOtterandGitHub df92f7ee42 fix(video): write faststart mp4/mov output from stabilize-video (#593)
Add -movflags +faststart for mp4/mov/m4v output from stabilize-video so
the stabilized result streams and previews progressively instead of
appearing broken or corrupted (moov atom was landing after mdat).

Fixes #588
2026-07-21 13:23:25 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a361f94584 chore(deps-dev): bump the dev-deps group across 1 directory with 6 updates (#586)
Bumps the dev-deps group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) | `2.5.3` | `2.5.4` |
| [semantic-release](https://github.com/semantic-release/semantic-release) | `25.0.7` | `25.0.8` |
| [turbo](https://github.com/vercel/turborepo) | `2.10.4` | `2.10.5` |
| [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) | `4.3.2` | `4.3.3` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.3.2` | `4.3.3` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.1.3` | `8.1.5` |



Updates `@biomejs/biome` from 2.5.3 to 2.5.4
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.4/packages/@biomejs/biome)

Updates `semantic-release` from 25.0.7 to 25.0.8
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v25.0.7...v25.0.8)

Updates `turbo` from 2.10.4 to 2.10.5
- [Release notes](https://github.com/vercel/turborepo/releases)
- [Changelog](https://github.com/vercel/turborepo/blob/main/RELEASE.md)
- [Commits](https://github.com/vercel/turborepo/compare/v2.10.4...v2.10.5)

Updates `@tailwindcss/vite` from 4.3.2 to 4.3.3
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.3/packages/@tailwindcss-vite)

Updates `tailwindcss` from 4.3.2 to 4.3.3
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.3/packages/tailwindcss)

Updates `vite` from 8.1.3 to 8.1.5
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.1.5/packages/vite)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-deps
- dependency-name: "@tailwindcss/vite"
  dependency-version: 4.3.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-deps
- dependency-name: semantic-release
  dependency-version: 25.0.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-deps
- dependency-name: tailwindcss
  dependency-version: 4.3.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-deps
- dependency-name: turbo
  dependency-version: 2.10.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-deps
- dependency-name: vite
  dependency-version: 8.1.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-20 10:26:04 +00:00
SnapOtterandGitHub e7d3862c34 docs: GPU-runs-on-CPU troubleshooting note (#587)
Surface how to verify NVIDIA CUDA acceleration and recover when AI tools fall back to CPU despite --gpus all. Adds a Verify GPU acceleration section to the deployment guide (check logs, reinstall the affected bundle to restore the GPU ONNX Runtime build) and a pointer from the getting-started NVIDIA tip. Addresses #490.
2026-07-20 18:02:39 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
16d8891fd5 chore(deps): bump the production-deps group with 15 updates (#585)
Bumps the production-deps group with 15 updates:

| Package | From | To |
| --- | --- | --- |
| [@fastify/cookie](https://github.com/fastify/fastify-cookie) | `11.1.1` | `11.1.2` |
| [@scalar/fastify-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/fastify) | `1.62.5` | `1.62.9` |
| [@sentry/node](https://github.com/getsentry/sentry-javascript) | `10.65.0` | `10.66.0` |
| [bullmq](https://github.com/taskforcesh/bullmq) | `5.80.2` | `5.80.9` |
| [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) | `5.10.0` | `5.10.1` |
| [posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node) | `5.41.0` | `5.45.2` |
| [zxing-wasm](https://github.com/Sec-ant/zxing-wasm) | `3.1.1` | `3.1.2` |
| [lucide](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide) | `1.24.0` | `1.25.0` |
| [@sentry/react](https://github.com/getsentry/sentry-javascript) | `10.65.0` | `10.66.0` |
| [fuse.js](https://github.com/krisk/Fuse) | `7.4.2` | `7.5.0` |
| [posthog-js](https://github.com/PostHog/posthog-js) | `1.399.2` | `1.404.1` |
| [react-colorful](https://github.com/omgovich/react-colorful) | `5.7.0` | `5.8.0` |
| [wavesurfer.js](https://github.com/katspaugh/wavesurfer.js) | `7.12.10` | `7.12.11` |
| [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.1085.0` | `3.1090.0` |
| [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) | `3.1085.0` | `3.1090.0` |


Updates `@fastify/cookie` from 11.1.1 to 11.1.2
- [Release notes](https://github.com/fastify/fastify-cookie/releases)
- [Commits](https://github.com/fastify/fastify-cookie/compare/v11.1.1...v11.1.2)

Updates `@scalar/fastify-api-reference` from 1.62.5 to 1.62.9
- [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.65.0 to 10.66.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.65.0...10.66.0)

Updates `bullmq` from 5.80.2 to 5.80.9
- [Release notes](https://github.com/taskforcesh/bullmq/releases)
- [Commits](https://github.com/taskforcesh/bullmq/compare/v5.80.2...v5.80.9)

Updates `fast-xml-parser` from 5.10.0 to 5.10.1
- [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.10.0...v5.10.1)

Updates `posthog-node` from 5.41.0 to 5.45.2
- [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.45.2/packages/node)

Updates `zxing-wasm` from 3.1.1 to 3.1.2
- [Release notes](https://github.com/Sec-ant/zxing-wasm/releases)
- [Changelog](https://github.com/Sec-ant/zxing-wasm/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Sec-ant/zxing-wasm/compare/v3.1.1...v3.1.2)

Updates `lucide` from 1.24.0 to 1.25.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.25.0/packages/lucide)

Updates `@sentry/react` from 10.65.0 to 10.66.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.65.0...10.66.0)

Updates `fuse.js` from 7.4.2 to 7.5.0
- [Release notes](https://github.com/krisk/Fuse/releases)
- [Changelog](https://github.com/krisk/Fuse/blob/main/CHANGELOG.md)
- [Commits](https://github.com/krisk/Fuse/compare/v7.4.2...v7.5.0)

Updates `posthog-js` from 1.399.2 to 1.404.1
- [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.399.2...posthog-js@1.404.1)

Updates `react-colorful` from 5.7.0 to 5.8.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/compare/5.7.0...v5.8.0)

Updates `wavesurfer.js` from 7.12.10 to 7.12.11
- [Release notes](https://github.com/katspaugh/wavesurfer.js/releases)
- [Commits](https://github.com/katspaugh/wavesurfer.js/compare/7.12.10...7.12.11)

Updates `@aws-sdk/client-s3` from 3.1085.0 to 3.1090.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.1090.0/clients/client-s3)

Updates `@aws-sdk/lib-storage` from 3.1085.0 to 3.1090.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.1090.0/lib/lib-storage)

---
updated-dependencies:
- dependency-name: "@fastify/cookie"
  dependency-version: 11.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: "@scalar/fastify-api-reference"
  dependency-version: 1.62.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: "@sentry/node"
  dependency-version: 10.66.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: bullmq
  dependency-version: 5.80.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: fast-xml-parser
  dependency-version: 5.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: posthog-node
  dependency-version: 5.45.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: zxing-wasm
  dependency-version: 3.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: lucide
  dependency-version: 1.25.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: "@sentry/react"
  dependency-version: 10.66.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: fuse.js
  dependency-version: 7.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: posthog-js
  dependency-version: 1.404.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: react-colorful
  dependency-version: 5.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: wavesurfer.js
  dependency-version: 7.12.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1090.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: "@aws-sdk/lib-storage"
  dependency-version: 3.1090.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-07-20 17:54:45 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
cb6f2e5a59 chore(deps): bump node from a25c993 to 5647be7 in /docker (#581)
Bumps node from `a25c993` to `5647be7`.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 22-bookworm
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-20 17:54:39 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ccbaa0ae54 chore(deps): bump debian from 30482e8 to 9344f8b in /docker (#583)
Bumps debian from `30482e8` to `9344f8b`.

---
updated-dependencies:
- dependency-name: debian
  dependency-version: bookworm
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-20 17:54:34 +08:00
SnapOtterandGitHub bda4db3f35 fix(ocr): unblock and harden accurate-OCR install (#552)
Two OCR-install fixes surfaced while verifying the accurate-OCR (v3 runtime) path end to end:

- Installer timeout must be a safe integer, not a performance.now() float. With the default INSTALL_MAX_MS this failed every accurate-OCR install via the app right after the download (masked by the unpublished runtime; CI drives install_runtime.py directly so it never surfaced). Fixed via remainingInstallerTimeoutMs().
- Classify an absent or forbidden runtime index (401/403/404/410) as OcrRuntimeNotPublishedError with a clear "Fast OCR still works" message instead of a raw HTTP 404, without retrying.

Refs #552
2026-07-20 13:17:17 +08:00
SnapOtterandGitHub 5558cf18b8 docs(guide): describe both library save modes in database.md and architecture.md (#580)
Closes #578. Rewrites the user file library save-mode description in the English database.md and architecture.md guides (independent-new by default, parent-linked on overwrite) and updates all 20 translated copies of each, with i18n_source_hash re-stamped so the parity gate stays green.
2026-07-19 22:59:34 +08:00
SnapOtterandGitHub 1113c761ea feat(library): wire save-mode into the five custom-client tool submitters (#577)
Closes #565. Wires the fileId/saveMode pair into the ocr, erase-object, remove-background, background-replace, and blur-background submitters so the library save-mode selector works for them; remove-background's two-phase effects route now auto-saves the final composite instead of the transparent intermediate.
2026-07-19 22:35:25 +08:00
SnapOtterandGitHub 4fea434859 chore(landing): retire the 2.0 launch banner and refresh the social preview (#576)
Removes the temporary 2.0 launch banner and README note, and refreshes the social/OG card to the current landing hero (synced to landing/web/docs og-image.png).
2026-07-19 21:23:48 +08:00
SnapOtterandGitHub 1bac663a2e feat(erase-object): optional high-quality diffusion inpainting bundle (#566)
Adds an opt-in High Quality mode to the Object Eraser, backed by a new inpaint-hq feature bundle (Stable Diffusion 1.5 inpainting via diffusers). The default fast LaMa path is unchanged. Both arch archives are published to deepsafe/feature-bundles and the manifest carries their real sha256/sizes.

Verified end to end: a fresh container pulls the bundle from HuggingFace, checksum-verifies it, extracts torch/diffusers plus the fp16 model, and the HQ sidecar erases a large object with a plausible fill.

Refs #141
2026-07-19 20:47:35 +08:00
SnapOtterandGitHub 84c18eb82c test(landing): guard _redirects against catalog drift (#575)
Exports a pure buildRedirects() from generate-redirects.mjs and adds a unit test asserting the committed apps/landing/public/_redirects matches it, so tool additions can't silently leave the generated redirects stale (see #573).
2026-07-19 16:36:33 +08:00
SnapOtterandGitHub 6339370093 fix(a11y): focus indicators meet the 3:1 non-text contrast bar (#574)
Fixes #568. Adds a real --color-ring token (ink orange #A85518 light / #F0A766 dark) and sweeps all 57 focus-indicator occurrences onto it: soft opacity rings blended to 1.2-1.7:1, border-only indicators sat at 2.6-3.0:1, and four focus:ring-ring sites referenced a token that never existed. Sponsor button keeps pink via pink-700; range sliders and the file list gain their missing keyboard indicators; landing skip link and form borders hardened; the palette contrast guard pins the ring at 3:1 in both themes.
2026-07-19 16:23:15 +08:00
SnapOtterandGitHub 51022628dc fix(a11y): WCAG AA contrast retune for the Otter Orange palette (#567)
Fixes #557. Vivid fill, ink label: brand #E07832 stays on fills while primary-foreground flips to #1A1814 (5.83:1); new theme-aware ink tokens carry orange, destructive, and success text roles; opacity-modified text purged; landing, demo, and the docs fund button retuned. Guarded by a CSS-parsing unit contrast test, rebuilt axe baselines with zero contrast entries, a new landing axe smoke, and fully regenerated darwin visual baselines.
2026-07-18 12:56:48 +08:00
SnapOtterandGitHub 6241f8719f chore(landing): regenerate _redirects for remove-gif-background (#573)
Resyncs the generated _redirects with the tool catalog; it drifted when remove-gif-background was added without rerunning scripts/generate-redirects.mjs. Landing-only PR, admin-merged past the path-skipped required checks.
2026-07-18 12:28:36 +08:00
SnapOtterandGitHub 1e1efbdfb3 test(landing): fix six stale e2e specs and wire the suite into CI (#572)
The landing Playwright suite ran in no CI workflow, so six specs had drifted red on main. Five subpages navigation tests asserted bare paths while the site emits trailing-slash URLs (format: directory), and one asserted a localized tool-detail page that is English-only by design. Fix the assertions and rewrite the tool test to the real invariant, then add a test-e2e-landing job gated on a new landing path filter so the suite runs on landing-relevant PRs and can't silently rot again.
2026-07-18 12:28:25 +08:00
SnapOtterandGitHub 69f72a6c71 fix(docs): keep the nav within the viewport on tablets and laptops (#570)
The custom nav cluster (theme toggle + Fund + GitHub Star) rendered inline at
every width, overriding VitePress's responsive collapse: a horizontal scrollbar
at 768-959px and off-screen clipping of the buttons on 1280-1366px laptops.

Show the custom cluster only at >=1440px where it fits, defer to VitePress's
native nav below that, anchor the flyout menu to the start edge in RTL, and drop
the redundant "Home" nav link so the nav fits at 768px.

Closes #556
2026-07-18 12:27:00 +08:00
SnapOtterandGitHub e3a01b6be6 fix(landing): open Product dropdown below the navbar bottom line (#571)
Anchor the Product menu panel below the navbar's bottom border line by bumping its top padding (pt-3 to pt-8). The panel was anchored to the button, landing 10px above the line, so the full-width navbar gradient line cut across the top of the open dropdown.
2026-07-18 11:53:51 +08:00
SnapOtterandGitHub a23158d968 feat(files): add save-as-new vs overwrite choice for library file edits (#564)
Editing a file from the library used to silently supersede it: the worker auto-saved every result as a new version and the leaf-only listing hid the original, which read as a destructive overwrite. Tool pages now show a per-edit choice for library-sourced files. The default saves the result as an independent new file and keeps the original; picking overwrite keeps the old superseding-version behavior.

The client sends a saveMode multipart field next to fileId, validated with a 400 on unknown values, and autoSaveToLibrary branches on it. Every hand-written route that honors fileId parses the field the same way as the factory. The review panel shows where an auto-saved result went instead of offering a second, duplicate save. Tools whose route or submitter ignores fileId keep the selector hidden via a shared unsupported-tools set, and the choice resets to the non-destructive default whenever a new file is staged.

Closes #495
2026-07-18 11:36:08 +08:00
SnapOtterandGitHub e113684ddb test(docs): make search and nav e2e tests hydration-aware (#551)
The docs e2e suite clicked navbar and sidebar controls before Vue hydrated the
multi-locale bundle, so the clicks were swallowed. That raced the Pagefind
search open (filed as #551), the appearance toggle, and the homepage and
sidebar navigation tests. The old search tests also matched an input
placeholder the config overrides, so they failed against a working build.

Add a waitForHydration helper (gates on #app.__vue_app__, set inside Vue's
app.mount()) and an openDocsSearch helper, and route the affected tests through
them. Search itself was never broken; this change is test-only. Docs e2e suite
is green (43/43).

Closes #551
2026-07-18 11:22:38 +08:00
SnapOtterandGitHub 67f54347b2 fix(landing): keep mixed-case locale casing in emitted URLs (#562)
Astro's getRelativeLocaleUrl lowercases the locale segment by default, so landing links and hreflang for zh-CN, zh-TW, and pt-BR were emitted lowercase and 404 on case-sensitive Cloudflare Pages. Pin the casing at the localizeHref chokepoint with normalizeLocale: false, add an e2e hreflang casing guard, and add a deploy-time check that blocks the build if any lowercased locale path leaks into the output.

Closes #554
2026-07-18 10:47:53 +08:00
SnapOtterandGitHub 54073a7c50 fix(landing,docs): use build-time GitHub star count, drop per-page api.github.com fetch (#560)
Removes the client-side api.github.com fetch from the landing navbar and the docs theme; both now render a build-time star count (docs via a new VitePress data loader). deploy-docs.yml gets GITHUB_TOKEN plus a daily refresh cron, mirroring deploy-landing.yml. Fixes #555.
2026-07-18 10:45:28 +08:00
SnapOtterandGitHub d4eaa655b2 fix(audio): expose sample rate setting in Convert Audio (#561)
The Convert Audio tool promised configurable bitrate, sample rate, and channel count, but only format and bitrate were exposed. Adds an optional sampleRate setting (8000 to 96000 Hz, omitted = preserve source) wired through the Zod schema, the FFmpeg -ar flag, the standalone settings panel, and the pipeline builder controls.

Impossible combinations fail loudly instead of degrading silently: MP3 + 96000 Hz is rejected (libmp3lame caps at 48 kHz), and MP3 bitrates above the encoder ceiling at low rates (64 kbps at 8 kHz, 160 kbps at 16/22.05 kHz) are rejected rather than clamped. The UI offers only legal combinations and sanitizes stored pipeline settings on load.

Docs updated in English plus all 20 localized pages with refreshed i18n_source_hash stamps; two new UI strings added to all 21 locales.

Fixes #558
2026-07-18 10:14:50 +08:00
SnapOtterandGitHub 6ecc598fc4 fix(landing): link English-only tool-detail and self-hosted pages to un-prefixed URLs (#553)
Tool-detail pages (/tools/<section>/<tool>/) and the /self-hosted pages are
built only in English, with no per-locale route, so a locale-prefixed link
404s in the static build. Add an enOnlyHref() helper and use it for those
links in Footer, Navbar, HeroSearch, and ToolGrid so localized pages point at
the English pages that actually exist. Adds an e2e guard asserting localized
pages emit un-prefixed URLs for those routes.
2026-07-18 00:01:03 +08:00
SnapOtterandGitHub 3f7214bac2 fix(web): use h-dvh for app shells so mobile tool controls stay reachable (#559)
On mobile browsers 100vh is the tall viewport (URL bar retracted), so the
full-height shells (h-screen + overflow-hidden) rendered their bottom strip
below the visible area with no way to scroll to it. After an upload the tool
page's bottom control is the "Process" peek bar, so it landed off-screen on
every tool.

Switch the fixed-height shells from h-screen (100vh) to h-dvh (100dvh), which
tracks the visible viewport. AppLayout covers every tool page; the same sweep
covers the editor, login, change-password, 404, and the error/loading screens.
On desktop dvh equals vh, so desktop is unchanged. Adds a source-level guard
(tests/unit/web/app-shell-viewport.test.ts) since headless Chromium has no URL
bar and cannot reproduce the bug.
2026-07-17 23:53:26 +08:00
dependabot[bot]andGitHub 80330a8b07 chore(deps): bump the actions group with 5 updates (#508)
Bumps 5 GitHub Actions via Dependabot. All 17 checks green.
2026-07-17 10:58:28 +08:00
SnapOtterandGitHub 8dcfd55745 docs(i18n): review pass over the low-resource guide translations (#549)
Independent reviewer agents checked all 20 locales of guide/low-resource plus the two cross-link paragraphs against the English source. 26 fixes across 12 locales (fr calques, tr/ru terminology drift, a zh-CN crop-vs-trim meaning error, a zh-TW garden-path sentence, ko particles, de collocations, es calques, small id/vi/th/sv fixes); 8 locales clean. Parity check and VitePress build pass.

Admin merge: docs-only PR, path-filtered required contexts never report (#420 precedent).
2026-07-17 10:23:54 +08:00
SnapOtterandGitHub 86251434b5 feat(telemetry): Sentry + PostHog quality pass (#546)
Comprehensive telemetry quality improvements across Sentry and PostHog, grounded in an audit of the live data plus current best-practice research.

Sentry: job_id/instance_id tags, operational fingerprinting, PII-safe settings context on bug events, web tag population + extension-noise filtering, an early-crash buffer, http status/method kept on breadcrumbs, and a gated-off-by-default performance-tracing re-enable (tracesSampler that zeroes db/redis/queue-poll root spans + drops the Redis integration) with worker job spans and canonical-host cron monitors.

PostHog: history_change SPA pageviews, instance_id super property for fleet rollups, enriched tool_used (formats, byte sizes, is_batch, execution_hint, real error_kind taxonomy), the previously-dead result_saved/batch_processed/ai_bundle_prompted events fired, search click-through, editor + Automate authoring + auth instrumentation, a before_send PII boundary, and minimal opt-in landing-site pageviews.
2026-07-17 01:51:48 +00:00
SnapOtterandGitHub 9247947704 fix(docs): localize the docs homepage, nav, and sidebar chrome across 20 languages (#547)
Localize the docs homepage (DocsHome.vue), top nav, and sidebar structural labels into all 20 non-English locales; individual tool names stay English by design. Also derive the homepage tool-count chips from the shared catalog via toolSection() so they no longer drift.
2026-07-17 09:18:26 +08:00
SnapOtterandGitHub fe85dd2b98 docs(guide): add a low-resource deployment guide in 21 languages (#548)
New guide/low-resource page: what runs well on 2 GB machines, a Raspberry Pi / old laptop Compose walkthrough with tuned caps, the env-var knobs that matter on small hardware, and what to skip. Linked from getting-started, the deployment hardware section, and the sidebar. Translated into all 20 non-English locales via the i18n batch pipeline; parity check and VitePress build pass.

Admin merge: docs-only PR, the path-filtered required integration contexts never report (#420 precedent).

Closes #497
2026-07-17 00:40:48 +08:00
SnapOtterandGitHub 1f4878ac4d fix(sign-pdf): surface PDF load failures instead of a blank canvas (#545)
A genuine pdf.js load failure (corrupt or password-protected file) was swallowed by the same catch that silences teardown rejections, leaving a blank canvas that looks like it is still loading. The two cases are now distinguished by the load effect's cancelled flag, and a real failure renders a clear message pointing at Unlock PDF for encrypted files. New loadFailed string in all 21 locales.

Item 4 of #478.
2026-07-17 00:36:48 +08:00
SnapOtterandGitHub c8629c9d22 fix(ai-bundles): stop CPU onnxruntime from clobbering onnxruntime-gpu (#544)
Both PyPI onnxruntime flavors unpack into the same site-packages directory, so a bundle carrying the CPU build (transcription, via faster-whisper) overwrote the GPU build's native libraries during install while the stale onnxruntime_gpu dist-info kept claiming otherwise. Every ONNX-backed tool then silently ran on CPU.

The installer now reconciles the flavor before the venv merge and the GPU build always wins, in both install orders; reinstalling any GPU bundle repairs a previously clobbered venv. gpu.py's warning now says exactly that. Build-side, build-bundle.sh gains the same reconcile and verify-bundle-compatibility.sh layers bundles through the real installer merge and asserts a single flavor.

Verified live on an RTX 4070 against the published bundles: reproduced the clobber with the stock installer, then confirmed both the prevention and repair paths with the patched one.

Fixes #490
2026-07-17 00:25:32 +08:00
SnapOtterandGitHub 846044a463 fix(settings): let admins relax the minimum password length to 1 (#543)
The password policy toggles (uppercase, lowercase, digit, special) can all be switched off in Settings -> Security, but the minimum-length input clamped at 4, so homelab admins couldn't deliberately allow short passwords. The API never enforced a floor; only the UI did. Lower the input floor to 1 and pin it with a test.

Closes #136
2026-07-17 00:22:45 +08:00
SnapOtterandGitHub 4448da9027 test(ocr): make the retry-abort deadline test deterministic (#542)
Defer the overall deadline until the first fetch is dispatched (the deferred-deadline pattern the sibling 20ms test already uses), so a loaded CI runner cannot abort before fetch and randomly fail Unit Tests.
2026-07-16 23:31:53 +08:00
SnapOtterandGitHub 4ac89fe650 fix(api): wait for Postgres and Redis at startup instead of crash-looping (#537)
Briefly retry Postgres and Redis connectivity at boot (waitForService, DB_STARTUP_TIMEOUT_MS) so an ordered-but-not-yet-ready dependency recovers cleanly instead of crash-looping.
2026-07-16 22:50:11 +08:00
SnapOtterandGitHub 43ddf96ccf fix(background-removal): surface failures with a diagnosable SafeError (#535)
removeBackground failures wrap in a SafeError so the specific reason survives the Sentry scrubber; the OOM lighter-model fallback and bridge SafeError passthrough are preserved.
2026-07-16 19:51:54 +08:00
SnapOtterandGitHub 281b4a06e3 feat(telemetry): add a safe input_format tag to worker error reports (#541)
Every worker tool error now carries an input_format tag (file extension only, never the filename) for triage, derived once at the worker error path and added to the scrubber allowlist.
2026-07-16 19:27:16 +08:00
SnapOtterandGitHub 631d82eaae fix(telemetry): classify environmental database errors as operational (#540)
Postgres auth (28xxx), permission (42501), resource (class 53), and operator-intervention (class 57) failures now classify as operational via a cause-chain walk, not bug. pg query bugs (e.g. 42601) stay bugs.
2026-07-16 19:26:46 +08:00