Commit Graph
98 Commits
Author SHA1 Message Date
anthony 4e58568ddd fix(deploy): mirror frontend runtime deps into the root package.json
The o2switch nodevenv/Passenger setup only installs a single
package.json/node_modules (root), so npm install --prefix frontend
never actually populates frontend/node_modules there. Node's module
resolution walks up from frontend/src to the root node_modules, so
adding react-router-dom, react-i18next, i18next, and
@phosphor-icons/react to root's dependencies (react/react-dom were
already present) lets the production vite build resolve them without
needing a second node_modules tree.
2026-07-31 15:26:16 +02:00
anthony 965b2ecc53 style(frontend): responsive polish pass for narrow viewports
Part of the Ombrora Convert redesign (Task 5/5).
2026-07-31 15:00:18 +02:00
anthony 8e405bd3b0 feat(frontend): add per-language SEO metadata, JSON-LD, robots.txt, and sitemap
Part of the Ombrora Convert redesign (Task 4/5).
2026-07-31 14:59:06 +02:00
anthony 2aa8e51b80 feat(frontend): add reassurance strip and supported-formats section
Part of the Ombrora Convert redesign (Task 3/5).
2026-07-31 14:57:46 +02:00
anthony bc7a3b0691 feat(frontend): add routing, layout, i18n wiring, and restyle the conversion tool
Migrates the existing upload/format-select/quality/convert logic from
the old single-file App.jsx into a router shell (/ -> /fr//en/) with a
shared Layout (header, language switcher, dark-mode toggle, footer).
Tool logic itself is unchanged, only restyled into cards.

Part of the Ombrora Convert redesign (Task 2/5).
2026-07-31 14:55:16 +02:00
anthony ee18cc2d90 feat(frontend): add design tokens, dark mode hook, and i18n scaffold
Part of the Ombrora Convert redesign (Task 1/5).
2026-07-31 14:49:19 +02:00
anthony f499196dae docs: add Ombrora Convert frontend redesign implementation plan
5-task plan: design tokens/dark mode/i18n scaffold, routing+layout+
migrated tool, reassurance+formats sections, SEO metadata, responsive
polish.
2026-07-31 14:37:05 +02:00
anthony ab95e3e699 docs: add Ombrora Convert frontend redesign design spec
Foundation sub-project: merged homepage+tool page, FR/EN i18n via URL
prefix, dark mode, SEO basics, and reassurance elements backed by
actual backend guarantees (1h auto-delete, 100MB max, no account).
2026-07-31 14:27:15 +02:00
anthony 5850ca2c68 README.md > Add ffmpeg portable o2switch setup 2026-07-31 14:01:27 +02:00
anthony d811eb0abc README.md > Add Calibre Deploy Method 2026-07-31 13:54:56 +02:00
anthony dd8557e418 docs: add ebook conversion implementation plan 2026-07-31 13:29:11 +02:00
anthony 86b555b334 feat: register ebook converter in the app so the API serves the new family
Also fixes a real bug found via the fb2 API test: file-type sniffs a
real fb2 file's XML declaration as generic "xml", not "fb2" and not
undetected, so it never reached the undetectable-format fallback added
in the previous commit. Added an fb2->xml alias in normalizeFormat,
same pattern as the existing azw3->mobi one.
2026-07-31 13:29:06 +02:00
anthony 977dac35d9 feat: add ebook MIME types, fix azw3/mobi detection collision, trust undetectable ebook formats 2026-07-31 13:25:46 +02:00
anthony 534630ab4e feat: give ebook conversions their own worker timeout and register the converter 2026-07-31 13:24:49 +02:00
anthony f796c0bd97 feat: add configurable ebookJobTimeoutMs to config 2026-07-31 13:23:30 +02:00
anthony b95e371989 feat: add ebook converter wrapping Calibre's ebook-convert CLI 2026-07-31 13:23:01 +02:00
anthony 0cef6842de docs: add ebook conversion design (Calibre ebook-convert wrapper) 2026-07-31 13:13:49 +02:00
anthonyandClaude Sonnet 5 e7c30202f5 feat: register font/dfont converters in the app and worker
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 12:59:19 +02:00
anthonyandClaude Sonnet 5 be50f1f076 feat: add font MIME types and fix dfont/ico detection collision
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 12:57:35 +02:00
anthonyandClaude Sonnet 5 2cde4ec035 feat: add source-only dfont font converter
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 12:56:13 +02:00
anthonyandClaude Sonnet 5 e5e4f31e9b feat: add bidirectional ttf/otf/woff font converter
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 12:55:19 +02:00
anthonyandClaude Sonnet 5 b61e6e2df3 feat: add font conversion dependencies and test fixtures
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 12:54:32 +02:00
anthony 1eb391242f Add implementation plan for font conversion (ttf/otf/woff/dfont)
Five TDD tasks: dependencies+fixtures, ttf/otf/woff converter,
dfont extraction converter, mime.js fixes, and wiring+API tests.
2026-07-31 10:58:53 +02:00
anthony eb211d5672 Fix fontkit import in font conversion spec
Verified empirically: fontkit has no default ESM export in this project's
"type": "module" setup (import fontkit from 'fontkit' throws). The working
form is import * as fontkit from 'fontkit'.
2026-07-31 10:53:07 +02:00
anthony bd13bc1c9a Add design spec for font conversion (ttf/otf/woff/dfont; cff/cid dropped)
Empirically verified library choices (fonteditor-core, opentype.js, fontkit)
against real fonts before committing to the scope, and root-caused a
file-type/dfont MIME collision that would otherwise silently break uploads.
2026-07-31 10:50:44 +02:00
anthonyandClaude Sonnet 5 25a95f1ea3 feat: add ICO size picker to the upload UI
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:18:07 +02:00
anthonyandClaude Sonnet 5 598781f417 feat: register ICO/HEIC/HEIF converters in the worker and pass iconSize through
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:16:25 +02:00
anthonyandClaude Sonnet 5 67bdfbf3de feat: wire ICO/HEIC/HEIF converters and iconSize validation into the API
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:15:14 +02:00
anthonyandClaude Sonnet 5 be43eac13a feat: thread iconSize through jobRepository
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:13:15 +02:00
anthonyandClaude Sonnet 5 1ff660a3d2 feat: add iconSize column to ConversionJob
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:12:32 +02:00
anthonyandClaude Sonnet 5 8795f65055 feat: add source-only HEIC/HEIF image converter
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:10:41 +02:00
anthonyandClaude Sonnet 5 05ef7dfca1 feat: add bidirectional ICO image converter
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:09:56 +02:00
anthonyandClaude Sonnet 5 39b296f5d3 feat: add ico MIME type and heif->heic extension alias
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 10:08:46 +02:00
anthony 2b2d8a9e6f Add implementation plan for HEIC/HEIF/ICO image format support
9 tasks covering mime.js, the ico/heic converter modules, a new
iconSize column on ConversionJob, app.js/worker.js wiring, and the
frontend size picker. Every code snippet (icojs encode/decode, the
heic-convert ESM import, prisma db execute/migrate status flags) was
verified against the actual installed/resolved packages rather than
guessed.
2026-07-31 09:54:06 +02:00
anthony fc217d8aae Add HEIC/HEIF test fixtures for upcoming heic-convert-based decoding
Reuses libheif's own LGPL/MIT-licensed with-alpha-512x512.heic sample
under both a .heic and .heif filename, verified to decode correctly
via heic-convert (the library the image format work will use). No
suitably-licensed HEVC/mif1-branded .heif-distinct sample exists that
is compatible with the pinned libheif-js version, and file-type detects
both extensions identically regardless of container brand, so the
duplicate content is sufficient to exercise the heif source-format
code path.
2026-07-31 09:48:11 +02:00
anthony 587ace3366 package.json > create deploy script 2026-07-31 09:31:13 +02:00
anthony bac93c853a Add design spec for HEIC/HEIF (source-only) and ICO image formats
Drops HDR from scope: the installed sharp/libvips build has it fully
disabled and no viable library exists for either direction.
2026-07-31 09:30:26 +02:00
anthony b40a7d340f Add support for csv + xlsx 2026-07-31 09:17:08 +02:00
anthony 4c408d83e9 Markdown conversion doc + CLAUDE.md improve 2026-07-31 08:40:04 +02:00
anthonyandClaude Sonnet 5 756e875fd9 feat: add md<->txt passthrough document converters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 08:34:51 +02:00
anthonyandClaude Sonnet 5 70c0c5ce6b feat: add md<->docx document converters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 08:33:31 +02:00
anthonyandClaude Sonnet 5 82533ca436 feat: add md<->pdf document converters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 08:31:53 +02:00
anthonyandClaude Sonnet 5 1595c5e0af feat: add md<->html document converters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 08:31:00 +02:00
anthonyandClaude Sonnet 5 28b6edac82 feat: add MIME plumbing for markdown (.md) format
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 08:29:55 +02:00
anthony 7d61209bd8 CLAUDE.md > add precisions for o2switch that need precompiled binaries 2026-07-31 08:28:14 +02:00
anthony 02ae3bc44b docs: add design spec for Markdown (.md) document conversion
Adds md<->html, md<->txt, md<->docx, md<->pdf via markdown-it and turndown.
2026-07-31 08:20:29 +02:00
anthony 15baa25c43 package.json > fix prisma generate for o2switch 2026-07-31 08:14:56 +02:00
anthonyandClaude Sonnet 5 e4507ca8a0 Fix broken migration fallback in CLAUDE.md; add defensive null guards
The documented shadow-database-free fallback for generating migration SQL
(--from-migrations ... --to-schema-datamodel ...) was never actually tested
and fails for the same P3014 reason `migrate dev` does, since
--from-migrations also requires --shadow-database-url internally. Verified
against the local dev DB that --from-schema-datasource (live introspection,
no shadow DB) diffed against --to-schema-datamodel (static file read) works
in both the no-op case and a real ADD COLUMN case, and documented that
instead. Also corrected the inaccurate claim that this matched Task 3's
approach (Task 3 used --from-empty) and moved the suggested SQL output path
out of the repo root into the OS temp directory.

Also brings markFailed's errorMessage/errorLog params in line with markDone's
existing ?? null guard, and adds orderBy to findExpiredJobs to match
findPendingJobs, for consistency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 02:05:37 +02:00
anthonyandClaude Sonnet 5 3c0de7e742 docs: fill migration-workflow gaps found in final review
CLAUDE.md:
- Add the one-time `prisma migrate resolve --applied 0_init` baseline
  step that must run before the first `migrate deploy` against any
  environment (o2switch production included) whose conversion_jobs
  table predates Prisma. Without it, migrate deploy tries to
  CREATE TABLE against a table that already exists and fails, leaving
  migration history stuck.
- Document the actual, tested result of `prisma migrate dev` against
  the local dev DB: it fails with P3014 because convert_user lacks
  CREATE DATABASE/DROP DATABASE privileges needed for the shadow
  database. Document the verified fallback (`migrate diff
  --from-migrations ... --to-schema-datamodel ...` + manual migration
  folder + `migrate resolve --applied`) as the supported way to create
  new migrations here.

README.md:
- Local development step 2 referenced the deleted db/schema.sql;
  replaced with the real `prisma migrate deploy` invocation, which
  creates conversion_jobs fresh on an empty local database. Reordered
  so `npm install` runs first, since the migrate command needs
  node_modules/@prisma/client.
- Deployment on o2switch: added the same one-time baseline step
  (clearly marked, not to be repeated) before the first migrate
  deploy on that server, and added `prisma migrate deploy` to the
  "every subsequent deployment" checklist, after npm install and
  before restarting the app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 01:54:23 +02:00
anthonyandClaude Sonnet 5 c42fe68b36 fix: isolate per-job cleanup errors and guard markDone against undefined
runCleanup previously had no try/catch around each expired job, so one
job's markCleaned/delete failure would abort the whole pass, leaving
later expired jobs' files undeleted for that run. Each iteration is now
wrapped in a try/catch that logs and continues; the returned count only
reflects jobs that actually completed the delete+markCleaned sequence.

markDone now guards outputPath/outputMimeType/outputSizeBytes/
conversionDurationSeconds with `?? null`, matching the guard createJob
already has on `quality` — Prisma treats `undefined` in a data object as
"leave the column alone" rather than binding NULL like the old raw SQL
did. Currently unreachable in practice since the worker always passes
real values, but keeps the repository defensive and consistent.

Added a cleanup.test.js case that monkey-patches
prisma.conversionJob.update to reject for one job's cleanedAt update,
asserting a later expired job in the same batch still gets cleaned.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 01:54:14 +02:00