13 KiB
13 KiB
Plan: Start-page redesign — PDF toolbox
Goal
Today the start page (src/routes/+page.svelte) offers exactly one entry point: the ZIP dropzone
that starts the beA archive workflow. This redesign turns the start page into a toolbox hub:
- The existing ZIP upload (beA workflow) stays available — visually prominent as the "hero" action of the product.
- Eight additional tools become reachable as tiles from the start page:
- Wasserzeichen — add a watermark to a PDF
- Signieren — sign a PDF
- Passwort setzen — encrypt a PDF with a password
- Passwort entfernen — unlock a password-protected PDF
- Konvertieren — convert a PDF
- Komprimieren — compress a PDF
- Seiten entfernen — remove pages from (separate) a PDF
- PDFs zusammenfügen — merge PDFs
All processing remains 100 % client-side (no uploads, no network requests, no persistence), in line with the project's privacy constraints.
Target UX
Start page (/)
- The Nominandum header/footer and overall visual language stay untouched.
- A compact hero area with the product name and a one-line value proposition.
- A large primary tile for the beA workflow ("ZIP-Archiv bearbeiten") — it keeps the full-height dropzone feel: dragging a ZIP anywhere on the start page still starts the workflow.
- A grid of secondary tool tiles (2–3 columns responsive,
grid+gap-*, white cards on the pale blue-gray background, Lucide icon + German title + one-line description, visible focus and keyboard operability — tiles are real links/buttons, not divs). - German copy throughout (project constraint).
Tool pages
- Each tool gets its own route under
src/routes/tools/…(see D1):tools/bea,tools/merge,tools/separate,tools/watermark,tools/sign,tools/encrypt,tools/decrypt,tools/convert,tools/compress. - A shared layout (
tools/+layout.svelte) provides a consistent shell: back-link to/, tool title, and the file-pick area. - The existing ZIP workflow component tree (
ZipDropzone,ProcessedZipArchiveEditor,SubDocumentEditor,AttachmentPreview) moves totools/beaessentially unchanged.
Technical approach per tool
| Tool | Route | Input | Processing | Output |
|---|---|---|---|---|
| beA workflow | tools/bea |
.zip (multiple) |
unchanged existing pipeline | merged/separate PDFs |
| Merge | tools/merge |
multiple PDFs | pdf-lib copy of pages in order | one PDF |
| Separate / remove pages | tools/separate |
one PDF | pdf-lib copy with page selection | one PDF |
| Watermark | tools/watermark |
one PDF | pdf-lib overlay on every page | one PDF |
| Sign | tools/sign |
one PDF | pdf-lib places signature image | one PDF |
| Encrypt | tools/encrypt |
one PDF | encryption library (see D5) | password-protected PDF |
| Unlock | tools/decrypt |
protected PDF + password | pdfjs (password) → rebuild (see D6) | unprotected PDF |
| Compress | tools/compress |
one PDF | pdfjs rasterize + re-encode (see D7) | smaller PDF |
| Convert | tools/convert |
one PDF | pdfjs render pages (see D4) | images or PDF variant |
1. Merge PDFs
- Load each selected PDF with pdf-lib, copy pages in the (reorderable) list order into a new
document. This is a generalization of the existing
mergeProcessedZipArchivelogic — extract a reusablemergePdfFiles(files: File[]): Promise<Uint8Array>helper.
2. Separate / remove pages
- Render page thumbnails (reuse the pdfjs pattern from
AttachmentPreview.svelte), show a page grid with selection; the user deselects/removes pages and downloads the remaining document. - Page selection must also work with keyboard focus (project accessibility rule).
3. Watermark
- Options: text (or uploaded PNG/JPG image), position (center/diagonal/corners), opacity, font
size, page range (all / selected). Drawn with pdf-lib
drawText/drawImageon each selected page. - No new dependency.
4. Sign
- Default proposal (see D3): visual signature. The user draws a signature in a canvas element (mouse/touch/pen), or types a name in a handwriting-like font, or uploads a signature image. The signature is exported as a transparent PNG and placed by clicking/tapping the desired position on a rendered page (pdfjs preview + pdf-lib placement).
- PAdES/cryptographic signing is not part of the default scope.
5. Set password (encrypt)
- pdf-lib does not support encryption → a new dependency is unavoidable (see D5).
Primary candidate:
@cantoo/pdf-lib(drop-in fork of pdf-lib with AES encryption support), alternative:qpdf-wasm. - UI: password + confirmation field, optional permissions flags (print/copy allowed?).
6. Unlock (remove password)
- pdfjs-dist can open password-protected PDFs when given the password; pdf-lib cannot load them.
- Default approach (see D6): open with pdfjs using the password, then rasterize each page to a canvas and rebuild a PDF via pdf-lib. Known tradeoff: text becomes images (no selectable text, larger file, no vector quality). This is documented to the user in the UI.
- Failing passwords must surface a clear German error message, not a console-only error.
7. Compress
- Browser-only compression is realistically rasterization-based: render each page with pdfjs at a chosen DPI/scale, encode as JPEG at a chosen quality, rebuild the PDF.
- UI presents target presets (e.g. "Starke Komprimierung" / "Ausgewogen" / "Leicht") and shows the resulting size estimate before download. Same vector→raster caveat as unlock, shown in the UI.
8. Convert
- Default scope (see D4): PDF → images (PNG or JPEG, one file per page, downloaded as separate files or a ZIP via fflate — fflate is already a dependency).
- Other directions (images/Office → PDF) already exist in the beA flow for images; extending convert beyond PDF→images is out of scope for v1.
Shared infrastructure (new src/lib pieces)
PdfDropzonecomponent — generalization ofZipDropzonewith configurableacceptfilter (.pdfvs.zip) and drag state.ZipDropzoneis refactored on top of it (or replaced).usePdfThumbnailshelper /PdfPageGridcomponent — shared page-thumbnail rendering for separate/sign/watermark/convert/compress, modeled onAttachmentPreview.svelte's memory rules (dynamic pdfjs import, byte copies before render, revoked object URLs, destroyed loading tasks).downloadBlob()helper — the current inlinedownloadPdfBytesin+page.sveltemoves to a sharedsrc/lib/download.tsand gains a generic filename/MIME signature.- Tool registry (
src/lib/tools.ts) — typed list of{ slug, title, description, icon }consumed by the start page tile grid, so adding a tool later means one entry + one route. - All browser-only work stays in event handlers /
onMount(SSR-safe, peragents.md). - File-size/memory guard: cap or warn on very large PDFs before rasterization-heavy tools (unlock/compress/convert).
Dependencies
- New runtime dependency (only for encrypt):
@cantoo/pdf-liborqpdf-wasm— decision D5. All other tools run on the existing stack (pdf-lib, pdfjs-dist, fflate).
Implementation phases
Each phase ends with pnpm check, pnpm lint, pnpm build and a manual browser pass
(prettier formatting on changed files).
Phase 0 — Groundwork
- Create
tools/+layout.svelteshell andsrc/lib/tools.tsregistry. - Move the existing ZIP workflow from
+page.sveltetotools/bea/+page.svelteunchanged (keep the settings popover, thumbnail width localStorage, bulk export). - Build the new start page: hero + beA tile + (initially) tool tiles that link to stub routes with a "coming soon" state? — no: see D8; default is to ship phases together and leave tiles hidden until their tool exists.
- Update
agents.mdrepository structure and flow sections.
Phase 1 — No-new-dependency tools
PdfDropzone,download.ts,PdfPageGrid/thumbnail helper.tools/merge(reusablemergePdfFileshelper).tools/separate(page grid, selection, export).tools/watermark.
Phase 2 — Signature
tools/sign: canvas signature capture (draw/type/upload), placement preview, export.
Phase 3 — Password tools
- Add the chosen encryption dependency;
tools/encrypt. tools/decrypt(pdfjs password + rasterized rebuild) with explicit quality warning in UI.
Phase 4 — Rasterization tools
tools/compress(DPI/quality presets, size preview).tools/convert(PDF → PNG/JPEG, separate files or ZIP).
Phase 5 — Polish
- Start-page tile copy and icons review (German, Nominandum visual language).
- Final
check/lint/build, updateagents.md, README feature list.
Open decisions (please confirm before implementation)
Defaults are marked ✅.
D1 — One page per tool vs. single-page app
- ✅ A. Separate routes (
/tools/<slug>). Better deep-linking/bookmarking, per-tool state is isolated, back-navigation is natural, and SvelteKit gives us the structure for free. - B. Single start page where each tile opens an in-place panel/modal. Keeps everything on
/but concentrates a lot of state in one component and hurts URL shareability.
D2 — Where does the ZIP workflow live?
- ✅ A.
/tools/bea, start page shows it as the large primary tile. Drag-a-ZIP-anywhere on the start page still routes into the workflow, preserving today's fastest path. - B. ZIP workflow stays on
/and tools branch off it. Rejected: makes/a mixed dropzone+dashboard and complicates the page.
D3 — What does "sign" mean?
- ✅ A. Visual signature (drawn/typed/uploaded image placed on the page). No crypto, no certificates, no PAdES.
- B. Cryptographic/PAdES signing. Large scope (certificate handling, new heavy dependencies, legal implications) — recommend a separate project if needed.
D4 — What does "convert" mean (v1)?
- ✅ A. PDF → PNG/JPEG (per-page images, optionally bundled into a ZIP with fflate).
- B. PDF → PDF/A. There is no solid browser-only PDF/A converter; would need a new heavy dependency or a server.
- C. Images/Office → PDF. Image→PDF already exists inside the beA flow; Office formats are out of scope.
D5 — Encryption dependency
- ✅ A.
@cantoo/pdf-lib— API-compatible fork of pdf-lib with encryption; smallest migration risk (drop-in swap of the pdf-lib import in the encrypt tool only). - B.
qpdf-wasm— battle-tested engine, but WASM bundle size and wiring cost are significant. - C. Server-side encryption — violates the no-upload constraint; rejected.
D6 — Unlock strategy
- ✅ A. pdfjs (with password) → rasterize pages → rebuild with pdf-lib. Works fully in the browser; document the vector→raster quality loss in the UI before the user commits.
- B. Await/patch pdf-lib fork decryption support — currently not available for browser use.
- C. Server-side qpdf — violates the no-upload constraint; rejected.
D7 — Compress strategy
- ✅ Rasterize-and-re-encode (pdfjs render + JPEG at chosen quality). Predictable, browser-only; shows the same quality caveat as unlock.
- B. "Structure-only" optimization (remove metadata, deduplicate objects). pdf-lib offers little here; savings are usually marginal. Could be added as a light pre-step within the same tool.
D8 — Release strategy for tiles
- ✅ A. Ship it whole / hide unfinished tiles. Only tiles whose route works are rendered (driven by the tool registry), so phases can be merged without dead links.
- B. Show all tiles immediately with "in Kürze verfügbar" badges — acceptable only if the redesign itself must go live before all tools are done.
Risks and mitigations
- Rasterization quality/memory (unlock, compress, convert): large PDFs can exhaust browser memory when rendering many pages at high DPI. Mitigate with sequential page processing, DPI caps, and an upfront file-size warning.
@cantoo/pdf-libmaturity: it is a fork; keep the pdf-lib import isolated to the encrypt tool so a swap back (or to qpdf-wasm) stays local.- Scope creep on "sign"/"convert": the open decisions above pin v1 scope deliberately; crypto signing and PDF/A conversion are explicitly out.
- Regression risk for the existing workflow: Phase 0 is a pure move of the current page; verify sub-document editing, XJustiz naming, and bulk export still work after relocation.