Fixes SeoHead (title/canonical/hreflang/JSON-LD) to render during SSR via
vite-react-ssg's Head component instead of a client-only useEffect, so
crawlers see real tags without executing JS. Also fixes an SSR crash in
useTheme (localStorage access with no window during server render) and
removes now-duplicated static meta tags from index.html.
Applies prisma/migrations/*/migration.sql on first container boot via a
sourced docker-entrypoint-initdb.d script, so a fresh container matches a
`prisma migrate deploy` run without needing shadow-database privileges.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Verified exact file-type v22 magic-byte detection for all 5 formats against
real fixture buffers (no normalizeFormat aliases needed), and simplified the
converter's resolution-scaling gate to match audio.js's validation-boundary
pattern (app.js range-checks, converter trusts the value).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every other family (images, documents, fonts, ebooks) had a showcase
pair in the marquee; archives was missing one after the archive
conversion feature landed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Also adds a --color-family-archives CSS variable (light + dark) that
the plan didn't call out explicitly but is needed for the new family
tile/icon to render with a color instead of falling back silently.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Also fixes a third double-extension bug the plan missed: app.js derived
each job's uuid via path.basename(file.filename, path.extname(...)),
which only strips the last dot segment. For a compound extension like
tar.gz this left "<uuid>.tar" as the "uuid", overflowing the uuid
column (Char(36)) and crashing the request. Replaced with
stripExtension(). Also bumped rateLimitMaxJobs in jobs.test.js's config
override — the file's cumulative POST /api/jobs calls across all tests
now exceeds the production default (20/window) once the archive tests
are included, causing spurious connection resets independent of any
real bug.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
archiver v8 (installed) uses a class-based API (`ZipArchive`) rather than
the factory-function API most examples online still describe — adjusted
createZip() to match after the first test run surfaced the mismatch.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets users drop an uploaded file from the UI list before or after
conversion, without cancelling anything server-side.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rebuild the visual identity from the actual Ombrora mark (violet/cyan aurora
blobs) instead of the generic blue/amber scheme, add Bricolage Grotesque and
IBM Plex Mono for display and code-like text, and give the hero an animated
aurora backdrop, gradient headline, and a format-pair marquee driven by the
real registered converters.
Rework the file config form: target format and icon size are both chip
groups now instead of a native select, and quality/compression sliders get a
gradient-filled track, custom thumb, and a mono value pill to match. File
type icons are color-coded by family (images/documents/fonts/ebooks) and
reused across FileConfigCard, FileCard, and FormatsGrid.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The download button was showing the input file's size (carried over
client-side from upload) instead of the actual converted file's size.
It now reads outputSizeBytes from the polled job status once done.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>