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).
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.
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.
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.
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.
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
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.
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.
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.
Switch the footer LiftOff badge image to lift-off.sh's hosted webp so their launch-verification step passes. Removes the unused self-hosted copy; the do-follow link is unchanged.
Adds a dedicated remove-gif-background AI tool: removes the background from an animated GIF, WebP, or APNG frame by frame and reassembles a transparent (or composited) animation in WebP, APNG, or GIF, with full per-frame effects. Reuses the background-removal bundle. Verified end-to-end with the real rembg model.
Closes#496.
The remove.bg comparison + tweaked alternatives rows, re-translated into all 20 languages but missed in the merge PR. Admin-merged (landing-only PR, skipped Integration matrix contexts never report; known repo deadlock). pnpm i18n:check passes on all 4 surfaces; landing build clean.
Render-blocking head script that routes a returning visitor to the language they explicitly picked (stored on switch). Only on default English pages; skips English-only tool-detail pages; crawler-safe. Admin-merged: landing-only PR, the skipped Integration matrix jobs never report the required contexts (known repo deadlock). Logic verified across 10 scenarios; landing build + typecheck clean.
Shared Claude Code translation pipeline (scripts/i18n, no API key) plus Astro/VitePress/Scalar i18n wiring. Landing and API reference translated into all 20 languages; docs i18n wiring + English source anchors. The translated docs markdown (apps/docs/<locale>/**, 3,620 files) follows in a companion PR because it exceeds GitHub's per-PR CI file limit.
Regenerate the social/OG card (200+ tools, Private file processing, self-hosted infrastructure) and sync to landing/web/docs; update banner, press kit, package + OpenAPI + Docker Hub descriptions, a leaked docs count, and the English About string.
Adds a CSS-only Product dropdown (Developers, Self-hosted tools, Alternatives) to the top nav and restores the lg breakpoint. Mobile menu lists items flat. Updated homepage e2e (navbar + dropdown-hover tests, footer scope). Verified at 1024/1280px, 63/63 landing e2e.
Restores Developers to the top nav by swapping out the Features homepage anchor. Nav is now Enterprise, Pricing, Alternatives, Developers, Docs, Talk to a human. Updated two e2e assertions that checked for a Features nav link. Verified visually at 1120/1280px and 62/62 landing e2e.
Fixes the broken top navbar (logo/Features collision, wrapping CTAs). Removes the Developers link (kept in footer), adds shrink-0/whitespace-nowrap, and shows the horizontal nav at min-[1120px] with a hamburger below. Verified visually at 1024/1120/1152/1280px; 62/62 e2e, build green. Admin merge: landing-src-only, required matrix contexts path-skip and never report.
Follow-up to #469. Homepage/enterprise/FAQ JSON-LD and meta now say 'file-processing infrastructure' (not 'suite'), and the visible homepage counts (ToolGrid subtitle, Pricing bullet) plus SEO meta say '200+'. Removed the orphaned toolCount/TOOLS references. Leaves the /tools catalog and ToolGrid filter-tab functional counts exact. Admin merge: landing-src-only PR, required matrix contexts path-skip and never report.
Leads public copy with self-hosted file-processing infrastructure and demotes tool count to a proof point across README, docs, llms.txt, DockerHub, and the landing site. Adds a /self-hosted hub plus 7 job-intent SEO pages with a build-time validator, deepens the flagship /alternatives pages, adds a remove.bg page, three shared components, and landing e2e coverage.
Add an OIDC / SSO login line to the Open Source tier and narrow the Enterprise bullet to SAML SSO + SSO enforcement. OIDC login ships in the free open-source build (env-gated, no license); only SAML and SSO enforcement are licensed.
Deep otter-brown Web 1.0 style bar pinned above the fixed navbar (offset via --lb-h), with a localStorage-remembered dismissal and a render-blocking check so returning visitors see no flash. Temporary; remove once 2.0 is old news.
Adds a click-to-copy Docker quickstart command and a small live-demo button to the landing hero, between the category cards and the stats. Removes the divider above the stats and tightens hero spacing so the stats stay above the fold. New HeroQuickstart.astro is a scoped copy-to-clipboard island (clipboard + execCommand fallback, aria-live, reduced-motion).
* docs: add 1.x-to-2.0 migration guide and upgrade notice
Adds MIGRATING.md with backup and upgrade steps, plus a short
"coming from 1.x?" callout in README and the docs upgrade guide
pointing existing users at it.
* fix: replace stale image-only and pre-rename data copy across product
SnapOtter grew from an image-only tool into a 5-modality suite
(Image, Video, Audio, PDF, Files), but copy in several places never
caught up. Fixes:
- dropzone.defaultFormats (i18n): every non-English locale still had
the pure pre-2.0 image-only format list; English omitted Files
entirely. Corrected across all 21 locales.
- settings.about.appDescription (i18n): "document, and data" workflow
copy updated to "PDF, and file" across all 21 locales.
- constants.ts: Files category's raw name was still "Data Files".
- Landing hero subtitle, JSON-LD schema, llms.txt, and 7 spots in the
competitor-comparison pages.
- Docs: VitePress config, supported-formats, deployment, and an
architecture.md modality-naming nit.
- OpenAPI description, root package.json description/keyword, and a
GitHub issue template dropdown option.
DOCKERHUB.md's separate "v1.x, image tools only" pre-release notice
is left untouched since 2.0 hasn't published to Docker Hub yet.
* test: update dropzone format-hint assertion to match corrected copy
The expected string still had the stale image-only/duplicated
PDF-Documents text from before the dropzone.defaultFormats fix.
* feat(api): parse DATA_DIR from env for 1.x import auto-detection
Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w
* test(migrator): build 1.17.2 fixtures by replaying legacy migrations
Discovered the legacy migrations seed a Default team (0005) and builtin roles
(0007), so the replayed fixture carries them. Seed uses a distinct custom team.
Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w
* fix(migrator): self-adjusting column copy, jobs.status map, drop sessions, advisory lock
The importer now inserts only the intersection of source and live target columns,
so the three analytics_* columns 2.x dropped no longer break the first users INSERT
(and future dropped columns are handled generically). jobs.status is mapped onto the
2.x enum (error->failed). Sessions are no longer migrated. A pg_advisory_xact_lock
serializes concurrent replicas. Includes login-after-migrate and library assertions.
Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w
* test(migrator): CI drift guard fails when a required column is unfillable from 1.17.2
Introspects every NOT-NULL-no-default column of each migrated table in the current
schema and asserts the engine can fill it from a real 1.17.2 source. Turns a future
breaking schema change into a PR-time failure instead of a production import break.
Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w
* feat(migrator): orchestrator with detection, boot states, marker, blob count
sqlite-import.ts owns source resolution (explicit path, 'off' sentinel, DATA_DIR
probe), the four boot states (import/leftover/locked/none), the persisted
sqlite_import marker, and a read-only library-blob count. runBootImport wires them
together and catches TargetNonEmptyError as a benign multi-replica skip.
Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w
* feat(api): route boot through the 1.x import orchestrator; hide marker from non-admins
index.ts now calls runBootImport (which owns detection + the four boot states)
instead of the inline SQLITE_MIGRATE_PATH block. The sqlite_import marker is added
to SENSITIVE_KEYS (but not REDACTED_KEYS) so admins see the counts for the banner
while non-admins don't see the key at all.
Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w
* feat(migrator): add analyzeSqlite + dry-run/verify CLI
analyzeSqlite is a read-only pre-flight (no live Postgres): per-table row counts,
library-blob presence, and out-of-enum job statuses. The migrate:sqlite CLI now
lives in the orchestrator and supports --dry-run/--verify (prints the analysis and
exits without writing) alongside the existing import and --force.
Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w
* docs: add 1.x to 2.0 upgrade guide; fix volume-name casing
New apps/docs upgrade guide covering auto-detect, the SQLITE_MIGRATE_PATH override +
off opt-out, the dry-run, what carries over, locked-state recovery, and non-destructive
rollback. Leads with 'back up the WHOLE /data volume, not just snapotter.db' because
1.x WAL mode leaves data in snapotter.db-wal (surfaced by the real-image upgrade test).
Standardizes README/DOCKERHUB compose volume names on the canonical SnapOtter-data
casing so they match the repo compose and don't orphan an upgrader's volume.
Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w
* feat(web): admin 1.x migration banner + 21-locale strings
A one-time admin banner reads the sqlite_import marker from /v1/settings and shows
the import result (user + saved-file counts) on success, or a warning when a 1.x
database was found but not imported. Dismissal persists to a sqlite_import.dismissedAt
settings key. shouldShowMigrationBanner/parseMigrationMarker sit in feedback.ts with
the other shouldShow helpers; strings added to en.ts and all 20 other locales.
Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w
* style(landing): biome-format Hero.astro trustBadges array
Pre-existing formatting drift on main (its Lint check was skipped on the merge that
introduced it); this PR's full Lint run surfaced it. Formatting-only, applied via
the repo's own biome formatter to unblock the required Lint check.
Claude-Session: https://claude.ai/code/session_01721WHAUGxnVk22qEeTub7w
Retitle the homepage hero to "File processing for privacy-sensitive
teams", drop the redundant infrastructure subline, and trim the trust
badges to the four foundational signals (self-hosted, open source,
air-gap capable, compliance-friendly).
Align the homepage title and meta description to the new positioning,
overriding the site-wide defaults for the homepage only.
Claude-Session: https://claude.ai/code/session_01EhgRGhvhNGJVQHcdFdcNMa
Match the drift-proof public phrasing used elsewhere (the Languages
stat already reads "20+"). Avoids the below-hero number going stale as
tools are added.
Claude-Session: https://claude.ai/code/session_01JQ8LmV8LPLi8yNTayHzSTQ
README:
- Add a one-command Quick Start above Key Features (single docker run) to show how fast setup is; rename the detailed section to Deployment and drop the duplicated one-liner.
- List TinyWow among the alternatives.
- Tighten the sponsor call to action.
Branding:
- Replace the social card's GDPR/HIPAA badges with Self-hosted, Privacy-sensitive, Compliance-friendly, Air-gap capable, Open source, matching the landing Hero.
- Regenerate branding/social-preview.png and the synced landing/web OG images (counts refreshed to the live catalog).
Claude-Session: https://claude.ai/code/session_01JQ8LmV8LPLi8yNTayHzSTQ
Book a Demo moves into the outline nav slot (Try Demo is removed), and a new Get Started Free primary CTA links to the docs getting-started guide in a new tab. Applies to both the desktop bar and the mobile menu.
Claude-Session: https://claude.ai/code/session_01STf4CGEkYKNwN26rMxVpn6
Give the contact calls-to-action a friendlier, more human voice. The nav
"Contact" link and the FAQ "Contact Us" button become "Talk to a human",
and the Enterprise pricing "Contact Sales" becomes "Let's talk". All links
still point to /contact; only the labels change.
Claude-Session: https://claude.ai/code/session_01MbQGV1k1mneFidgGyDnmDQ
A self-hosted animated warm gradient now sits behind the hero: a captured
WebGL mesh loop (webm + mp4 + webp poster, no framework or Three.js) in a
top band that fades to cream before the tool search. The headline goes
white, badges become dark glass, and the navbar is more opaque so nav,
badges, headline, and subtitle all stay legible over the gradient.
Reduced-motion and small screens fall back to the still poster.
Also carries the hero copy already present in the working tree (new
headline, badge labels, and subtitle), which is entangled with these edits.
Claude-Session: https://claude.ai/code/session_01MbQGV1k1mneFidgGyDnmDQ
Landing:
- Preload the Bricolage display font (LCP heading) to cut first paint
- Cap tool-page meta descriptions at ~160 chars via whole-sentence
truncation, so SERP snippets no longer overflow or cut mid-phrase
- Drop dead HowTo + FAQPage JSON-LD from tool pages (Google removed
HowTo rich results in 2023 and FAQ rich results in 2026); the visible
content stays. Breadcrumb + WebApplication schema retained
- Shorten 22 tool titles that exceeded the ~60-char SERP limit
- Add an optional h1 override to tool SEO data
- Rewrite adjust-colors, smart-crop, and ringtone-maker to match real
Search Console query intent (saturation; object-aware cropping;
convert audio to ringtone), with query-matched H1s and FAQs
Docs:
- Add meta descriptions to 20 pages missing frontmatter
Repo:
- Ignore local credential JSON files (.secrets/, *client_secret*, *service_account*)