Commit Graph
1837 Commits
Author SHA1 Message Date
SnapOtterGitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
717de2577a chore(deps): bump the production-deps group across 1 directory with 18 updates (#326)
Bumps the production-deps group with 18 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@scalar/fastify-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/fastify) | `1.59.3` | `1.60.0` |
| [@sentry/node](https://github.com/getsentry/sentry-javascript) | `10.57.0` | `10.59.0` |
| [bullmq](https://github.com/taskforcesh/bullmq) | `5.78.1` | `5.79.1` |
| [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) | `5.9.0` | `5.9.3` |
| [ipaddr.js](https://github.com/whitequark/ipaddr.js) | `2.3.0` | `2.4.0` |
| [papaparse](https://github.com/mholt/PapaParse) | `5.5.3` | `5.5.4` |
| [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) | `8.21.0` | `8.22.0` |
| [playwright](https://github.com/microsoft/playwright) | `1.60.0` | `1.61.0` |
| [posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node) | `5.37.0` | `5.38.2` |
| [sharp](https://github.com/lovell/sharp) | `0.35.1` | `0.35.2` |
| [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro) | `6.4.7` | `6.4.8` |
| [lucide](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide) | `1.18.0` | `1.21.0` |
| [@sentry/react](https://github.com/getsentry/sentry-javascript) | `10.57.0` | `10.59.0` |
| [posthog-js](https://github.com/PostHog/posthog-js) | `1.386.6` | `1.391.9` |
| [react-image-crop](https://github.com/dominictobias/react-image-crop) | `11.0.10` | `11.1.2` |
| [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) | `7.17.0` | `7.18.0` |
| [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.1068.0` | `3.1073.0` |
| [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) | `3.1068.0` | `3.1073.0` |



Updates `@scalar/fastify-api-reference` from 1.59.3 to 1.60.0
- [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.57.0 to 10.59.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.57.0...10.59.0)

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

Updates `fast-xml-parser` from 5.9.0 to 5.9.3
- [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.9.0...v5.9.3)

Updates `ipaddr.js` from 2.3.0 to 2.4.0
- [Changelog](https://github.com/whitequark/ipaddr.js/blob/main/Changes.md)
- [Commits](https://github.com/whitequark/ipaddr.js/compare/v2.3.0...v2.4.0)

Updates `papaparse` from 5.5.3 to 5.5.4
- [Release notes](https://github.com/mholt/PapaParse/releases)
- [Changelog](https://github.com/mholt/PapaParse/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mholt/PapaParse/compare/5.5.3...5.5.4)

Updates `pg` from 8.21.0 to 8.22.0
- [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianc/node-postgres/commits/pg@8.22.0/packages/pg)

Updates `playwright` from 1.60.0 to 1.61.0
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.60.0...v1.61.0)

Updates `posthog-node` from 5.37.0 to 5.38.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.38.2/packages/node)

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

Updates `astro` from 6.4.7 to 6.4.8
- [Release notes](https://github.com/withastro/astro/releases)
- [Changelog](https://github.com/withastro/astro/blob/astro@6.4.8/packages/astro/CHANGELOG.md)
- [Commits](https://github.com/withastro/astro/commits/astro@6.4.8/packages/astro)

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

Updates `@sentry/react` from 10.57.0 to 10.59.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.57.0...10.59.0)

Updates `posthog-js` from 1.386.6 to 1.391.9
- [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.386.6...posthog-js@1.391.9)

Updates `react-image-crop` from 11.0.10 to 11.1.2
- [Release notes](https://github.com/dominictobias/react-image-crop/releases)
- [Commits](https://github.com/dominictobias/react-image-crop/compare/11.0.10...11.1.2)

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

Updates `@aws-sdk/client-s3` from 3.1068.0 to 3.1073.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.1073.0/clients/client-s3)

Updates `@aws-sdk/lib-storage` from 3.1068.0 to 3.1073.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.1073.0/lib/lib-storage)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1073.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: "@aws-sdk/lib-storage"
  dependency-version: 3.1073.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: "@scalar/fastify-api-reference"
  dependency-version: 1.60.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: "@sentry/node"
  dependency-version: 10.59.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: "@sentry/react"
  dependency-version: 10.59.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: astro
  dependency-version: 6.4.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: bullmq
  dependency-version: 5.79.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: fast-xml-parser
  dependency-version: 5.9.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: ipaddr.js
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: lucide
  dependency-version: 1.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: papaparse
  dependency-version: 5.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-deps
- dependency-name: pg
  dependency-version: 8.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: playwright
  dependency-version: 1.61.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: posthog-js
  dependency-version: 1.391.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: posthog-node
  dependency-version: 5.38.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: react-image-crop
  dependency-version: 11.1.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: react-router-dom
  dependency-version: 7.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-deps
- dependency-name: sharp
  dependency-version: 0.35.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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-22 22:16:14 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c8371c3cd1 chore(deps-dev): bump the dev-deps group with 5 updates (#297)
Bumps the dev-deps group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@playwright/test](https://github.com/microsoft/playwright) | `1.60.0` | `1.61.0` |
| [@testcontainers/postgresql](https://github.com/testcontainers/testcontainers-node) | `12.0.2` | `12.0.3` |
| [@testcontainers/redis](https://github.com/testcontainers/testcontainers-node) | `12.0.2` | `12.0.3` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `22.19.21` | `22.20.0` |
| [vitepress-plugin-llms](https://github.com/okineadev/vitepress-plugin-llms) | `1.13.1` | `1.13.2` |


Updates `@playwright/test` from 1.60.0 to 1.61.0
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.60.0...v1.61.0)

Updates `@testcontainers/postgresql` from 12.0.2 to 12.0.3
- [Release notes](https://github.com/testcontainers/testcontainers-node/releases)
- [Commits](https://github.com/testcontainers/testcontainers-node/compare/v12.0.2...v12.0.3)

Updates `@testcontainers/redis` from 12.0.2 to 12.0.3
- [Release notes](https://github.com/testcontainers/testcontainers-node/releases)
- [Commits](https://github.com/testcontainers/testcontainers-node/compare/v12.0.2...v12.0.3)

Updates `@types/node` from 22.19.21 to 22.20.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `vitepress-plugin-llms` from 1.13.1 to 1.13.2
- [Release notes](https://github.com/okineadev/vitepress-plugin-llms/releases)
- [Commits](https://github.com/okineadev/vitepress-plugin-llms/compare/v1.13.1...v1.13.2)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.61.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-deps
- dependency-name: "@testcontainers/postgresql"
  dependency-version: 12.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-deps
- dependency-name: "@testcontainers/redis"
  dependency-version: 12.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-deps
- dependency-name: "@types/node"
  dependency-version: 22.20.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-deps
- dependency-name: vitepress-plugin-llms
  dependency-version: 1.13.2
  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-06-22 21:42:30 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8c2ddf8b3d chore(deps): bump actions/checkout in the actions group (#295)
Bumps the actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 6.0.3 to 7.0.0
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 21:42:25 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1a6c3a25ac chore(deps): bump node from 2d178f2 to e0d149b in /docker (#294)
Bumps node from `2d178f2` to `e0d149b`.

---
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-06-22 21:42:21 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b3a2996f83 chore(deps): bump mwader/static-ffmpeg from 8.1.1 to 8.1.2 in /docker (#293)
Bumps mwader/static-ffmpeg from 8.1.1 to 8.1.2.

---
updated-dependencies:
- dependency-name: mwader/static-ffmpeg
  dependency-version: 8.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 21:42:17 +08:00
SnapOtterandGitHub 95d100c20b feat(web): in-canvas zoom & pan for the object eraser and split tools (#320)
* feat(web): add pure zoom/pan math module with unit tests

* feat(i18n): add a11y.pan key across all locales (English, matching adjacent zoom labels)

* feat(web): add useZoomPan hook (state + gestures over pure math)

* feat(web): add ZoomToolbar component

* feat(web): zoom & pan in the object eraser canvas

* feat(web): zoom & pan in the split tool preview

* fix(web): synchronous pan-mode refs so drag-pan is race-free under fast input

* test(e2e): zoom & pan acceptance (split always-on, eraser bundle-gated)
2026-06-22 20:50:28 +08:00
SnapOtterandGitHub 313d4ae04e test(e2e): fix stale route + fixture paths in erase-object spec (#321)
The spec used the bare /erase-object route (404 under section URLs) and the
old flat fixture path (test-200x150.png moved to image/valid/), so every test
silently skipped. Point it at /image/erase-object and the correct fixture, and
add a 404 guard so future route rot fails loudly instead of skipping.
2026-06-22 20:24:07 +08:00
SnapOtterandGitHub 5b86eaa2c5 chore(branding): multi-modal press kit, hero OG + dashboard.gif (#323)
- Recolor banner/wordmark SVGs to the Otter Orange palette with brand fonts
- Rebuild social-preview.png as a hero-style OG card (trust badges, headline, five modality cards with section counts), synced to apps/landing + apps/web og-image.png
- Replace static dashboard PNGs with dashboard.gif: a guided tab tour of all 157 tools across the five modalities
- Update branding/README.md (Otter Palette, brand fonts, asset list); add scripts/branding generator + sync helpers
2026-06-22 19:51:35 +08:00
SnapOtterandGitHub c4ce5f96df fix(i18n): translate Swedish (sv) UI strings (#310)
Translate the ~950 previously-untranslated Swedish UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 19:39:19 +08:00
SnapOtterandGitHub 4de24b8c9e fix(i18n): translate German (de) UI strings (#308)
Translate the ~950 previously-untranslated German UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 19:39:15 +08:00
SnapOtterandGitHub 3900350ad5 fix(i18n): translate Brazilian Portuguese (pt-BR) UI strings (#307)
Translate the ~950 previously-untranslated Brazilian Portuguese UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 19:39:10 +08:00
SnapOtterandGitHub fe5c7c8c86 fix(i18n): translate French (fr) UI strings (#306)
Translate the ~950 previously-untranslated French UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 19:39:05 +08:00
SnapOtterandGitHub b3ff8accec fix(i18n): translate Spanish (es) UI strings (#301)
Translate the ~950 previously-untranslated Spanish UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 19:39:00 +08:00
SnapOtterandGitHub 29fcc87708 fix(i18n): restore accents in Italian loading-message arrays (#322)
The auth.rotatingPhrases and features.progressMessages arrays were skipped by
the main Italian pass (#298) and still had a few missing accents (e.g. verb
e -> è). Diacritic-only fix; wording, placeholders, and key parity unchanged.
2026-06-22 19:38:54 +08:00
SnapOtterandGitHub f74f648e47 fix(docker): copy patches/ before pnpm install so cold builds succeed (#300)
The builder and production stages run `pnpm install` without first COPYing the
patches/ directory, so package.json's patchedDependencies makes pnpm abort with
`ENOENT: ... patches/gray-matter@4.0.3.patch` on any build whose pnpm-store
layer cache is cold (e.g. a fresh CI runner or `docker compose build`). The
existing image only built because that layer happened to be cached.

Copy patches/ ahead of both `pnpm install` invocations. Verified: a clean
`docker build` of docker/Dockerfile now completes end to end.
2026-06-22 16:59:10 +08:00
SnapOtterandGitHub 1fec97111b fix(docker): make storage writable under non-root/foreign UIDs (TrueNAS, OpenShift) (#299)
The entrypoint only fixed volume permissions when started as root (chown +
gosu-drop to snapotter). Launched under a non-root/foreign UID (TrueNAS app
user, Kubernetes runAsUser, OpenShift) it did no permission setup, so /data and
/tmp/workspace -- owned by uid 999 from the image -- were not writable by the
running user. Uploads and processing then failed with a cryptic EACCES
("workspace folder is not writable") and AI bundle installs failed the same way,
while health checks still reported the container healthy.

- entrypoint: source new entrypoint-lib.sh; verify writability up front when
  non-root, and as snapotter after chown when root (catches root-squashed
  mounts), failing fast with an actionable message (which dir, uid/gid, how to
  fix) instead of a late, cryptic EACCES
- Dockerfile: own /data and /tmp/workspace as snapotter:0, group-writable with
  setgid, so an arbitrary UID with the root supplementary group (OpenShift /
  Kubernetes fsGroup) can write; keep /opt/venv world-readable for the AI venv
  bootstrap under arbitrary UIDs
- api: assert storage writability at boot (lib/storage-writable.ts), failing
  fast with the same guidance even when the entrypoint is bypassed
- docs: add a Storage permissions section (named volumes, bind mounts, TrueNAS,
  Kubernetes/OpenShift) and cross-link it from the security guide

Fixes #230
2026-06-22 16:58:59 +08:00
SnapOtterandGitHub 5a6368db89 fix(i18n): translate Thai (th) UI strings (#319)
Translate the ~950 previously-untranslated Thai UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:44:12 +08:00
SnapOtterandGitHub 8a8a4f99a4 fix(i18n): translate Indonesian (id) UI strings (#318)
Translate the ~950 previously-untranslated Indonesian UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:44:08 +08:00
SnapOtterandGitHub 470272169d fix(i18n): translate Vietnamese (vi) UI strings (#317)
Translate the ~950 previously-untranslated Vietnamese UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:44:04 +08:00
SnapOtterandGitHub 00c89e4ae9 fix(i18n): translate Hindi (hi) UI strings (#316)
Translate the ~950 previously-untranslated Hindi UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:44:01 +08:00
SnapOtterandGitHub 8c1c2c310e fix(i18n): translate Turkish (tr) UI strings (#315)
Translate the ~950 previously-untranslated Turkish UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:43:56 +08:00
SnapOtterandGitHub ab37505e68 fix(i18n): translate Arabic (ar) UI strings (#314)
Translate the ~950 previously-untranslated Arabic UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:43:52 +08:00
SnapOtterandGitHub 6ee1b0f7a1 fix(i18n): translate Ukrainian (uk) UI strings (#313)
Translate the ~950 previously-untranslated Ukrainian UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:43:49 +08:00
SnapOtterandGitHub 7ce9c64b50 fix(i18n): translate Polish (pl) UI strings (#312)
Translate the ~950 previously-untranslated Polish UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:43:45 +08:00
SnapOtterandGitHub eb9a14a226 fix(i18n): translate Russian (ru) UI strings (#311)
Translate the ~950 previously-untranslated Russian UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:43:41 +08:00
SnapOtterandGitHub d036ff6d44 fix(i18n): translate Dutch (nl) UI strings (#309)
Translate the ~950 previously-untranslated Dutch UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:43:37 +08:00
SnapOtterandGitHub e7b412fc09 fix(i18n): translate Korean (ko) UI strings (#305)
Translate the ~950 previously-untranslated Korean UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:43:33 +08:00
SnapOtterandGitHub d8b037c8c6 fix(i18n): translate Japanese (ja) UI strings (#304)
Translate the ~950 previously-untranslated Japanese UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:43:30 +08:00
SnapOtterandGitHub de4d2ed166 fix(i18n): translate Traditional Chinese (zh-TW) UI strings (#303)
Translate the ~950 previously-untranslated Traditional Chinese UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:43:26 +08:00
SnapOtterandGitHub 26d8a0685c fix(i18n): translate Simplified Chinese (zh-CN) UI strings (#302)
Translate the ~950 previously-untranslated Simplified Chinese UI strings (tool names,
descriptions, settings labels, dialogs) that were still showing English, and
restore the {size} placeholder dropped from settings.aiFeatures.diskUsage.

Machine-translated and verified: full key parity with en.ts, all {placeholders}
preserved, passes tsc and Biome. Native-speaker review welcome.
2026-06-22 16:43:22 +08:00
SnapOtterandGitHub c0a8b36dcb fix(i18n): complete and correct Italian translation (#231) (#298)
Around 35% of Italian strings (903 of 2583) were still English while the
file silently typechecked (key parity was already correct). This translates
them and fixes quality issues in the existing Italian:

- Translate untranslated tool names, descriptions, categories and UI labels;
  keep legitimate English terms (formats, URL, Team, Pipeline, device presets)
- Fix accent errors (qualita->qualità, piu->più, Si e->Si è, and similar)
- Restore the {size} placeholder dropped from settings.aiFeatures.diskUsage
- Make role labels consistent Italian: Utente / Editore / Amministratore
- Normalize stray curly apostrophes to the file's straight-quote convention

Based on the Italian translation contributed by @albanobattistella (the issue
author), reconciled against the current en.ts (e.g. the Data->Files rename)
and corrected for accents and a structural error in the source.

Closes #231
2026-06-22 16:43:18 +08:00
SnapOtterandGitHub 33671d2d92 feat(landing): auto-refresh live GitHub stars and image-pull stats (#292)
Fetch landing stars + image pulls at build time via a shared stats lib, refreshed by a daily cron + authenticated GITHUB_TOKEN. Image Pulls totals live Docker Hub pull_count + a maintained GHCR estimate (ghcr.io has no public pull-count API).
2026-06-22 14:11:48 +08:00
SnapOtterandGitHub 9b1c105089 test(integration): accept 202 async for DNG in format-matrix smoke tests (#291)
* test(integration): accept 202 async for DNG in format-matrix smoke tests

Follow-up to #290. Full-resolution DNG decode (3474x2314 vs the old 1024px
preview) pushes expensive operations (AVIF encode, image-enhancement) past
the 8s sync window on CI runners, so the API correctly returns 202 Accepted
and processes the job asynchronously. The format-matrix smoke tests only
allowed [200, 400, 422] and required a clean error body on any non-200, so
they failed on the 202 (Integration shard 4/4 went red on main).

202 (accepted, async) is a valid clean response for these "no crash / clean
response" checks. Make the matrix allowlists 202-tolerant and require an
error body only for true error codes:
- add 202 to ACCEPTABLE_CODES / ACCEPTABLE_FALLBACK_CODES + inline allowlists
- change `if (statusCode !== 200)` error-body checks to `>= 400`

Verified locally against Postgres+Redis: DNG tests pass both normally (200)
and with SYNC_WAIT_MS=1 forcing 202 (68 passed, 0 failed each run).

* test(integration): treat 202 as non-error in DNG conversion else-branches

The first pass added 202 to status allowlists and switched `if (!== 200)`
error checks to `>= 400`, but missed the `if (200) {...} else {...}` shape in
the exotic conversion matrix and the expanded color-blindness test: their
`else` caught 202 and then asserted body.error (which async responses lack).

Reproduced locally by temporarily lowering the test sync-window floor to force
202 on every DNG op, then fixed every flagged assertion. Change the two
`else` branches to `else if (statusCode >= 400)` so 202 (accepted, async) is a
valid outcome with no sync body to verify.

Verified: forced-202 across all 4 DNG matrix files = 123 passed / 0 failed;
normal sync window = 34 DNG tests passed; typecheck + biome clean.
2026-06-22 11:39:13 +08:00
SnapOtterandGitHub 3d9ff1e0d2 fix(api): decode RAW via LibRaw first so DNG processes at full resolution (#289) (#290)
RAW (DNG) processing crashed on ImageMagick's deprecated ufraw-batch
delegate, which fails on modern formats such as iPhone ProRAW DNG.

Root cause: the dcraw_emu (LibRaw) decode tier read the wrong output path.
dcraw_emu APPENDS the output extension (raw-in-X.dng -> raw-in-X.dng.tiff)
but the code looked for raw-in-X.tiff (replaced extension), so readFile threw
on every RAW, the tier silently fell through to ufraw, and the 24MB TIFF
leaked into the temp dir on each attempt.

- Repair the dcraw_emu output path; clean it up in finally (fixes the leak)
- Prefer LibRaw full decode over embedded-preview extraction so a
  full-resolution RAW is never silently returned as a reduced-size preview
  (sample DNG: was 1024x683 preview, now 3474x2314 full)
- Add RAW decode regression tests (DNG full-resolution + all 6 RAW formats);
  these were absent, which let the bug ship
- Install libraw-bin on CI test runners so dcraw_emu is actually exercised
2026-06-22 09:54:04 +08:00
Luciano GodoyandGitHub ce02ce1348 fix(api): respect RATE_LIMIT_PER_MIN for tool routes (#272)
Tool endpoints (/api/v1/tools/*) now honor the RATE_LIMIT_PER_MIN env var instead of a hardcoded 60/min: `0` disables per-tool limiting, `>0` uses the configured value, and unset falls back to 60. Merged on top of the section-based route refactor (#280).

Fixes #271.
2026-06-21 23:39:55 +08:00
SnapOtterandGitHub 5d5117acf7 fix(deps): close js-yaml DoS alert + document rembg non-reachability (#286)
* fix(deps): patch gray-matter onto js-yaml 4.2.0 (close js-yaml DoS alert)

js-yaml 3.14.2 (quadratic-complexity DoS in merge-key handling, GHSA
patched only in 4.2.0) was kept in the tree by a scoped pnpm override
"gray-matter>js-yaml": "^3.14.1" that exempted gray-matter from the
global js-yaml>=4.2.0 override. gray-matter is a build-time-only
transitive dep of the docs site (vitepress-plugin-llms,
@sugarat/theme-shared) and pinned 3.x because it calls the removed
yaml.safeLoad / yaml.safeDump APIs.

Remove the exemption so gray-matter resolves js-yaml 4.2.0, and add a
pnpm patch renaming safeLoad->load / safeDump->dump (the 4.x
equivalents; load is safe by default). js-yaml 3.x is now gone from the
lockfile.

Verified: gray-matter parse+stringify smoke test passes on 4.2.0; full
VitePress docs build green (177 pages, llms plugin parses all tool
frontmatter with no safeLoad/safeDump error).

* docs(ai): document rembg 2.0.69 pin and advisory non-reachability

The patched rembg 2.0.75 pulls a numpy 2.x closure (numpy>=2.3,
scipy>=1.16, scikit-image>=0.26) that is incompatible with the
numpy==1.26.4-locked AI stack (realesrgan 0.3.0 and codeformer-pip 0.0.4
break on numpy 2.x). Both open rembg advisories are unreachable in this
codebase: rembg is used purely as a library (never the `rembg s`
server), and new_session() only receives allowlisted model names
(remove_bg.py ALLOWED_MODELS), never user-controlled paths. Record this
rationale next to the pin; the Dependabot alerts are dismissed as
not_used.
2026-06-21 23:24:10 +08:00
SnapOtterandGitHub c203267866 fix(docker): patch OS + pip image CVEs, document accepted Trivy residuals (#288)
Reduces the container-image CVE surface flagged by Trivy.

Genuinely fixed on every rebuild:
- apt-get upgrade in the production stage pulls Ubuntu security patches
  for base-image packages (libgnutls30t64 3.8.3-1.1ubuntu3.5 -> ubuntu3.6,
  libgcrypt20, liblzma5), closing ~15 OS-package CVEs.
- pip 25.1.1 -> 26.1.2 closes 4 pip CVEs (CVE-2025-8869, 2026-1703,
  2026-3219, 2026-6357).

Accepted via .trivyignore (canonical, reviewed):
- 6 newly surfaced pnpm 9.x build-tool CVEs (fixed only in pnpm 10.x, a
  major migration tracked separately; pnpm runs at install/start only).
- caire's bundled golang.org/x/image (esimov/caire v1.5.0 is latest and
  still pins x/image v0.18.0; no upstream fix).
- brace-expansion 2.x ReDoS (transitive of glob; patched 5.0.6 already
  present; not reachable from user input).

Already resolved in the current tree (clear on next scan): picomatch
4.0.4 (override), ip-address removed.

Verification note: the Trivy job in release.yml depends on the
intentionally gated-off docker build/publish job, so these cannot be
re-scanned in CI without enabling image publishing. The image is not
currently shipped.
2026-06-21 23:23:03 +08:00
SnapOtterandGitHub dba8a85a80 fix(jobs): pre-warm QueueEvents to kill first-sync-wait flake (#285)
The csv-json integration test intermittently timed out at 30000ms on
the first worker-backed job in a fork. Root cause: waitForJob() creates
the BullMQ QueueEvents consumer lazily on first use, and a fresh consumer
reads the Redis events stream from "$" (the tail at the moment its run
loop starts). A trivial tool can publish its completed:<id> event before
the brand-new consumer positions itself, so waitUntilFinished() never
sees the event and blocks for the full sync-wait window. In tests
SYNC_WAIT_MS is floored at 30000ms, exactly the vitest per-test budget,
so the stall surfaces as an opaque timeout instead of a 202 fallback.
This is also a latent production latency bug: the first synchronous tool
request after each boot could hang up to the 8s prod window.

Fix: warmQueueEvents() eagerly constructs and connects every pool's
consumer at spine startup, before any job is enqueued, so each consumer
is positioned at the stream tail up front and never misses a completion.
Awaited in the test spine (deterministic for the first request) and fired
non-blocking at prod boot (a slow Redis must not stall startup).

Adds a regression guard in job-spine.test.ts that drops the cached
consumers, warms explicitly, and asserts a fast job's completion is
captured on the first sync-wait.

Verified: 3 parallel stress runs (276 file-runs across all pools), zero
timeouts; targeted job-spine + csv-json suites green; typecheck clean.
2026-06-21 23:22:59 +08:00
SnapOtterandGitHub f75cc328ac fix(security): numeric CIDR matching for IPv6 SSRF allow/deny
Replace textual prefix matching in isPrivateIPv6() with numeric bit-prefix matching via ipaddr.js. Closes reachable classifier bypasses for IPv4-compatible IPv6 (::a.b.c.d), site-local fec0::/10, and the full fe80::/10 link-local span, on top of the IPv4-mapped forms. Embedded IPv4 (mapped and compatible) is run through the full isPrivateIPv4 classifier; unparseable input fails closed.

Follow-up hardening to f64cbdda.

Reported-by: tonghuaroot
2026-06-21 23:20:28 +08:00
SnapOtter 7a3b4e6b3b fix(security): basename-sanitize file-preview paths (CodeQL js/path-injection)
The resolve()+startsWith containment check was correct but CodeQL did not
recognize it. Apply path.basename() to the name in resolveWithinPreviewDir -- a
sanitizer CodeQL recognizes -- so every id-derived preview path is provably a
single filename inside the preview dir. Behaviour is unchanged for valid ids
(already charset-validated); containment check kept as a backstop.
2026-06-21 14:32:16 +08:00
SnapOtter bdadb843d8 fix(security): close remaining high-severity CodeQL alerts
- svg-sanitize.ts: strip each dangerous element repeatedly until stable with
  whitespace-tolerant end tags, defeating nested/overlapping tags (closes 5
  incomplete-multi-character-sanitization + 1 bad-tag-filter; the prior
  single-pass regex could leave a residual <script>/<iframe>).
- file-preview.ts: add a resolve()+containment barrier (the path-traversal
  guard CodeQL recognizes) on top of the id charset check (closes 9
  path-injection).
- metadata.ts: bound the XMP namespace:name key segments so parseXmp cannot
  backtrack polynomially (closes js/polynomial-redos).
- analytics-disabled.spec.ts: match analytics by URL host, not substring
  (closes 4 incomplete-url-substring-sanitization).

typecheck + lint green; svg (119), preview (22), metadata (164) tests pass.
2026-06-21 13:47:22 +08:00
SnapOtter 4fdd10f488 revert(deps): keep rembg at 2.0.69 (2.0.75 conflicts with pinned numpy==1.26.4)
rembg 2.0.75 requires a numpy incompatible with the pinned numpy==1.26.4
that the rest of the ML stack (onnxruntime etc.) depends on, making
pip-audit's resolution impossible. The rembg <2.0.75 advisory (medium) is
accepted as a residual: it only affects the on-demand background-removal AI
bundle (publishing currently paused) and can't be patched without a numpy
2.x migration across the whole Python sidecar.
2026-06-21 12:52:27 +08:00
SnapOtter 5662532501 fix(deps): drop undici override (broke jsdom@29)
jsdom@29.1.1 imports undici's internal lib/handler/wrap-handler.js, removed
in 8.5.0 -- so forcing undici>=8.5.0 broke ~27 jsdom unit files. Removing the
override lets pnpm resolve undici to the latest patched in-range versions
(6.27.0/7.28.0/8.5.0, all above the vulnerable ranges) while jsdom keeps
working. Unit suite restored to 188 files / 4908 tests.
2026-06-21 12:23:19 +08:00
SnapOtter 76fc695233 fix(benchmark): use section-prefixed tool URLs for 2.0 route scheme
The 2.0 API routes changed from /api/v1/tools/<toolId> to
/api/v1/tools/<section>/<toolId>. Updated all bench script call sites
to pass section-prefixed tool IDs (e.g. "image/resize" instead of
"resize"), matching the authoritative toolSection() mapping.
2026-06-21 12:18:47 +08:00
SnapOtter 2927e0d4be fix(e2e): disable Astro Dev Toolbar during landing e2e tests
The Astro Dev Toolbar (enabled by default in dev mode) injects h1
elements ("No islands detected", "Audit", "Settings") and a
"Community" link inside its shadow DOM. Playwright CSS selectors
pierce shadow DOM, causing 6 test failures:

- 5 heading-hierarchy tests saw extra h1 elements from the toolbar
- footer "Community" column title resolved to 2 elements (footer h4
  plus toolbar's Astro community chat link)

Root-cause fix: set PLAYWRIGHT=1 env var in the Playwright webServer
command; Astro config conditionally disables devToolbar when set.
Normal development retains the toolbar.
2026-06-21 12:13:48 +08:00
SnapOtter f21667db67 chore(deps): patch vulnerable dependencies (Dependabot/CodeQL)
- dompurify >=3.4.11 (runtime SVG sanitization)
- nanoid 4.x -> >=5.0.9 (vulnerable 4.0.x transitive; 3.x/5.x kept)
- undici >=8.5.0 (dev-only: jsdom/vitest/semantic-release; removes 8.4.1)
- rembg 2.0.69 -> 2.0.75 (Python AI sidecar, CPU + GPU)

js-yaml is already >=4.2.0; the residual 3.14.2 is gray-matter's build-time
pin (no 3.x patch exists). typecheck + build pass.
2026-06-21 11:59:26 +08:00
SnapOtter ae4fc1decf fix(security): explicit per-route rate limits (CodeQL js/missing-rate-limiting)
All /api/ routes are already covered by the global @fastify/rate-limit
(index.ts:283), but CodeQL doesn't model the global registration and flagged
every route without an explicit per-route limit. Added tuned config.rateLimit
to 45 routes: stricter on auth/credential routes (mfa/saml/oidc/api-keys,
15-30/min), moderate on writes (60/min), generous on reads/polls (300/min =
the global default). Real defense-in-depth on sensitive routes. 3 alerts on
non-route code (a preHandler hook, the rate-limiter's own DB lookup, a test
helper) are documented false-positives covered by the global limiter.
2026-06-21 11:49:02 +08:00
SnapOtter c1cd8712f4 fix(security): close file-preview path-injection + tighten subtitle detection
- file-preview.ts: validate the :id URL param against a safe charset and
  restrict the original-name extension to alphanumerics before they feed
  filesystem paths (closes 9 CodeQL js/path-injection; defense-in-depth on
  top of the existing DB lookup).
- media-input.ts: require the SRT/VTT timecode structure to detect a subtitle
  rather than a bare '-->' (closes CodeQL js/bad-tag-filter; also rejects
  non-subtitle files that merely contain '-->').
2026-06-21 11:31:39 +08:00
SnapOtter a6837b687a fix(e2e,landing): robust path escaping (CodeQL) + unique file-tools card
- tests/e2e/helpers.ts: build the sharp script path via JSON.stringify
  instead of single-quote-only replace (CodeQL js/incomplete-sanitization,
  high: backslashes were not escaped). Proper fix, no suppression.
- landing CategoryCards: rename the file-modality marketing card to
  "File Tools" (matches the Image/Video/Audio Tools siblings and is
  unique vs the 23 "Files" tool pills, which broke the e2e locator).
  Modality label stays "Files" everywhere it is the actual modality.
2026-06-21 03:25:45 +08:00
SnapOtter 6e1b9cd3cc fix(lint): resolve #280 Lint failures (route formatting + landing import sort)
#280 left 10 section-prefixed custom-route files mis-indented and one
unsorted import block in the new landing section-index page. Fixed via
biome formatter (api) and manual import sort (landing). No config change
(biome.json is hook-protected); no suppression. pnpm lint + typecheck
now exit 0.
2026-06-21 02:58:46 +08:00