feat(deps): pdfjs-dist + react-pdf for consistent in-app PDF preview

Replaces the `<iframe src={presignedUrl}>` preview path which
delegated rendering to the browser's built-in PDF viewer. The iframe
worked on desktop but failed on mobile (older Android Chrome
refuses inline PDFs; iOS Safari opens a new tab).

`<PdfViewer>` renders via pdfjs-dist + react-pdf so the experience
is identical across all browsers + form factors. Adds page nav,
zoom controls, and per-page accessibility labels.

Lazy-loaded via next/dynamic with ssr:false — pdfjs is ~150kb gzip,
no route ships it unless a PDF is actually previewed.

pdfjs worker + CMaps + fonts loaded from unpkg CDN pinned to the
matched pdfjs-dist version (first-load cost paid once per user, no
bundle-size impact on routes that never preview a PDF).

Verified: tsc clean, vitest 1315/1315, next build green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 22:56:42 +02:00
parent 75920a2540
commit d0a3a054b6
4 changed files with 366 additions and 3 deletions

View File

@@ -90,6 +90,7 @@
"p-limit": "^7.3.0",
"papaparse": "^5.5.3",
"pdf-lib": "^1.17.1",
"pdfjs-dist": "^5.7.284",
"pdfkit": "^0.18.0",
"pino": "^10.3.1",
"pino-pretty": "^13.1.3",
@@ -102,6 +103,7 @@
"react-hook-form": "^7.75.0",
"react-image-crop": "^11.0.10",
"react-number-format": "^5.4.5",
"react-pdf": "^10.4.1",
"react-resizable-panels": "^3.0.6",
"recharts": "^3.8.1",
"sharp": "^0.34.5",