Skip to content

New formats, offline app, landing pages and exact stats - #6

Merged
HapticHash merged 2 commits into
masterfrom
claude/capabilities-discussion-hrjot1
Oct 2, 2026
Merged

HapticHash merged 2 commits into
masterfrom
claude/capabilities-discussion-hrjot1

Conversation

@HapticHash

Copy link
Copy Markdown
Owner

Summary

Adds new file formats and options, usability features, an installable offline app, SEO landing pages, exact stats, and browser tests in CI.

Deploy note: the Cloudflare deploy command must be npx wrangler deploy. The stats counter is now a Durable Object, and its first deploy applies a migration that wrangler versions upload can't run.

Compression

  • Target-size presets (WhatsApp 16 MB, Discord 10 MB, Email 25 MB, Forms 200 KB, 100 KB) with a KB/MB unit switch
  • Before/after comparison slider for images
  • WebP and AVIF output (AVIF encoded with jSquash in a web worker); optional max dimensions
  • iPhone HEIC/HEIF input (heic-to)
  • Animated GIF compression that keeps every frame (gifsicle-wasm-browser)
  • PDF Keep text selectable mode (new default): only the images inside the PDF are re-compressed, so text, links and layout stay intact. The previous rasterizing mode is now Smallest size. Both run in a worker, with a fallback to the main thread. pdf.js fonts, cMaps and wasm decoders are served from /pdfjs/.
  • Video: MP4, WebM (VP8) or GIF output, 1080p/720p/480p, remove audio, trim per file
  • "Keep photo metadata" toggle; rows say when location and camera data was removed

Usability

  • Cancel per file, paste files (Ctrl+V), drop whole folders, warning before closing the tab mid-run
  • Settings remembered between visits, batch summary, per-file level, share button (Web Share API)
  • System/light/dark theme toggle (all colors are now theme tokens)
  • The title no longer reloads the page

Installable app and privacy

  • Service worker (src/sw.ts, vite-plugin-pwa): offline app shell, cache-on-first-use for lazily loaded chunks and the FFmpeg core, Android share target
  • Self-hosted Nunito Sans font and image-compression worker, so there are no Google Fonts or jsdelivr requests
  • "0 bytes of your files uploaded" badge
  • COEP changed from require-corp to credentialless

Growth

  • 11 static landing pages (e.g. /compress-pdf-to-100kb/, /heic-to-jpg/), each with its own title, description, canonical URL, static content and preset
  • sitemap.xml, robots.txt, og:image, PWA icons (scripts/generate-images.mjs)
  • Optional Cloudflare Web Analytics when CF_ANALYTICS_TOKEN is set at build time

Engineering

  • Stats moved from KV read-modify-write to a SQLite-backed Durable Object (worker/stats-counter.ts), so concurrent reports can't overwrite each other. Existing KV totals are imported on first use.
  • Playwright end-to-end suite (tests/e2e) against the production build, as a separate CI job

Not included

  • Multi-threaded FFmpeg: @ffmpeg/core-mt (0.12.10 and 0.12.6) crashes with "null function or function signature mismatch" in Chromium on a simple encode, so the single-threaded core stays.
  • VP9: libvpx-vp9 hangs in the WebAssembly build, so WebM output uses VP8.

Testing

  • npm run lint, npm test (15 unit tests), npm run build: pass
  • npm run test:e2e (10 Playwright tests, Chromium): pass. Covers every file type, PDF page count and size, AVIF/WebP plus the compare modal, a 100 KB custom target, video to GIF with trim, GIF compression, cancel, remembered settings and theme, landing-page metadata and preset, offline reload, and the share target.
  • wrangler dev: KV totals carried over to the Durable Object; 50 concurrent reports summed exactly (150 / 5500); invalid input returns 400. A wrangler deploy --dry-run also succeeds.
  • Not verified: FFmpeg caching by the service worker (the test environment intercepts unpkg requests before the service worker sees them), Safari/Firefox, a real Android share sheet, and dropping an actual folder.

🤖 Generated with Claude Code

https://claude.ai/code/session_011UUBS4XF57trCnZcGwBu35


Generated by Claude Code

Compression
- Target-size presets (WhatsApp, Discord, Email, Forms, 100 KB) with KB/MB units
- Before/after comparison slider for images
- WebP and AVIF output (AVIF via jSquash in a worker), resize by longest side
- HEIC/HEIF input (heic-to), animated GIF compression (gifsicle)
- PDF "keep text selectable" mode that only re-compresses embedded images;
  the rasterizing mode is now "smallest size"; both run in a worker with a
  main-thread fallback, and pdf.js fonts/cMaps/wasm are served from /pdfjs
- Video: MP4/WebM(VP8)/GIF output, resolution, remove audio, per-file trim
- Keep-metadata toggle; rows say when location/camera data was removed

Usability
- Cancel per file, paste files, drop folders, warn before leaving mid-run
- Settings remembered between visits, batch summary, per-file level
- Share button (Web Share API), dark mode with system/light/dark toggle
- Title no longer reloads the page

PWA and privacy
- Service worker: offline app shell, cached chunks and FFmpeg core,
  Android share target
- Self-hosted Nunito Sans and image-compression worker (no Google Fonts or
  jsdelivr); "0 bytes uploaded" indicator
- COEP credentialless instead of require-corp

Growth
- Static landing pages per use case with their own title, description,
  canonical URL and presets; sitemap.xml, robots.txt, og:image, icons
- Optional Cloudflare Web Analytics via CF_ANALYTICS_TOKEN at build time

Claude-Session: https://claude.ai/code/session_011UUBS4XF57trCnZcGwBu35
- Stats totals now live in a SQLite-backed Durable Object, which handles
  one request at a time, so concurrent reports no longer overwrite each
  other. Existing KV totals are imported on first use.
- Playwright end-to-end suite against the production build: every file
  type, AVIF/WebP, custom target, video to GIF with trim, GIF compression,
  cancel, remembered settings, landing pages, offline and share target.
  Runs as a separate CI job.
- Landing-page presets are no longer saved as the remembered settings
  until the user changes something.
- README: new features, privacy details, test commands, and why deploys
  must use `wrangler deploy` (Durable Object migration).

Claude-Session: https://claude.ai/code/session_011UUBS4XF57trCnZcGwBu35
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
compressly 0d833bf Oct 02 2026, 01:10 AM

HapticHash commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner Author

Workers Builds: compressly is red on 0d833bf. The first build was cancelled; the re-run (build 361571d3) failed. Its log is only visible in the Cloudflare dashboard.

The code builds: I reproduced Cloudflare's steps on this head with Node 24 (npm clean-install, npm run build, npx wrangler deploy --dry-run and npx wrangler versions upload --dry-run), and all succeed. The GitHub checks (check, e2e) are green.

Without the log, the two likely causes are both Cloudflare build settings, not code:

  1. Deploy command set to npx wrangler pages deploy dist. That's a Pages command and fails on any commit of this Worker ("Missing Pages project name"). It should be npx wrangler deploy.
  2. Non-production branch deploy command is npx wrangler versions upload (the Workers Builds default for PR branches). That command can't create the new StatsCounter Durable Object. This one clears once the PR is merged and npx wrangler deploy runs on master.

If the log shows something else, I'll fix it here.


Generated by Claude Code

@HapticHash
HapticHash merged commit f6f5c3d into master Oct 2, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant