Files
bea-edit/plan/done/start-page-toolbox-plan.md
T
2026-10-06 22:52:35 +02:00

13 KiB
Raw Blame History

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:

  1. The existing ZIP upload (beA workflow) stays available — visually prominent as the "hero" action of the product.
  2. 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 to tools/bea essentially 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 mergeProcessedZipArchive logic — extract a reusable mergePdfFiles(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/drawImage on 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)

  • PdfDropzone component — generalization of ZipDropzone with configurable accept filter (.pdf vs .zip) and drag state. ZipDropzone is refactored on top of it (or replaced).
  • usePdfThumbnails helper / PdfPageGrid component — shared page-thumbnail rendering for separate/sign/watermark/convert/compress, modeled on AttachmentPreview.svelte's memory rules (dynamic pdfjs import, byte copies before render, revoked object URLs, destroyed loading tasks).
  • downloadBlob() helper — the current inline downloadPdfBytes in +page.svelte moves to a shared src/lib/download.ts and 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, per agents.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-lib or qpdf-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

  1. Create tools/+layout.svelte shell and src/lib/tools.ts registry.
  2. Move the existing ZIP workflow from +page.svelte to tools/bea/+page.svelte unchanged (keep the settings popover, thumbnail width localStorage, bulk export).
  3. 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.
  4. Update agents.md repository structure and flow sections.

Phase 1 — No-new-dependency tools

  1. PdfDropzone, download.ts, PdfPageGrid/thumbnail helper.
  2. tools/merge (reusable mergePdfFiles helper).
  3. tools/separate (page grid, selection, export).
  4. tools/watermark.

Phase 2 — Signature

  1. tools/sign: canvas signature capture (draw/type/upload), placement preview, export.

Phase 3 — Password tools

  1. Add the chosen encryption dependency; tools/encrypt.
  2. tools/decrypt (pdfjs password + rasterized rebuild) with explicit quality warning in UI.

Phase 4 — Rasterization tools

  1. tools/compress (DPI/quality presets, size preview).
  2. tools/convert (PDF → PNG/JPEG, separate files or ZIP).

Phase 5 — Polish

  1. Start-page tile copy and icons review (German, Nominandum visual language).
  2. Final check/lint/build, update agents.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-lib maturity: 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.